{
  "id": 603779,
  "title": "[EN/日本語] 19th solution writeup",
  "url": "/competitions/cmi-detect-behavior-with-sensor-data/discussion/603779",
  "author_name": "tokiwa",
  "post_date": "2025-09-04T10:46:12.881000",
  "votes": 16,
  "comment_count": 0,
  "views": 0,
  "content": "<p>（下に日本語による説明を書いています）</p>\n<p>We would like to express our deepest gratitude to the competition hosts! We really enjoyed the competition for the entire period. <br>\nThis write-up is separated in two parts: the observation, preprocessing and postprocessing by <a href=\"https://www.kaggle.com/chimaki821\" target=\"_blank\">@chimaki821</a> then modeling, deep leaning by <a href=\"https://www.kaggle.com/tokkiwa\" target=\"_blank\">@tokkiwa</a>. </p>\n<h1>1. <a href=\"https://www.kaggle.com/chimaki821\" target=\"_blank\">@chimaki821</a> Part (Observation + Preprocessing)</h1>\n<p>I created this discussion using <a href=\"https://translate-demo.plamo.preferredai.jp/\" target=\"_blank\">PLaMo Translations</a>.</p>\n<h2>1.1. Overview</h2>\n<p>Like most participants, I implemented the following techniques:</p>\n<ul>\n<li>Corrected left-handed data by inverting <code>acc_y</code>, <code>rot_x</code>, and <code>rot_y</code> values, swapping THM channels 3↔5, swapping ToF channels 3↔5, and inverting an 8×8 square region</li>\n<li>Implemented multitask learning by adding <code>orientation</code> and <code>behavior</code> as auxiliary loss terms</li>\n<li>Switched between IMU-only and full sensor model configurations</li>\n<li>Applied corrections to subjects suspected of having mounting anomalies (partially successful, details follow)</li>\n<li>Used weighted ensemble methods (though not included in the final submission, these proved effective in private testing)</li>\n</ul>\n<p>After implementing these baseline approaches, my teammate <a href=\"https://www.kaggle.com/tokkiwa\" target=\"_blank\">@tokkiwa</a> significantly improved the model through various enhancements to the pipeline and ensemble methods with multiple models. This proved extremely valuable since I had virtually no background in machine learning.</p>\n<p>Below, I describe several key techniques that helped inform the above approach, as well as additional methods I implemented:</p>\n<ul>\n<li>Data observation and mounting correction</li>\n<li>Data augmentation (DA) and test-time augmentation (TTA) using rotation around the y-axis</li>\n<li>Creation of soft labels</li>\n</ul>\n<p>I also summarize:</p>\n<ul>\n<li>What I tried during the competition that didn't work or was left unimplemented</li>\n<li>Additional approaches I wanted to explore but couldn't implement in time</li>\n</ul>\n<h2>1.2. Data Observation and Mounting Correction</h2>\n<p>This section was developed in collaboration with <a href=\"https://www.kaggle.com/epopaca\" target=\"_blank\">@epopaca</a>.</p>\n<p>We created a <a href=\"https://www.kaggle.com/code/chimaki821/visualization-for-19th-place-solution\" target=\"_blank\">notebook</a> that, when provided with a specific subject and gesture, consolidates all relevant data into a single image. 81 subjects × 18 gestures = 1,458 images saved on my smartphone, constantly observing them whenever I had time.</p>\n<p>e.g. (SUBJ_000206, gesture: Above Ear - pull hair)<br>\n<img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F13641953%2F7da4b813f511805d152e32eb71f95fd9%2Fsubj_000206_gesture0.png?generation=1757046751796413&amp;alt=media\" alt=\"\"></p>\n<p>Using this visualization, we conducted analyses on left-hand correction and mounting adjustments. By examining <code>acc_x, acc_y, acc_z</code> values, we determined that <code>SUBJ_019262</code> and <code>SUBJ_045235</code> likely had improper mounting. Considering potential issues with the Herios sensor's attachment, there are four main possible scenarios (including normal conditions):<br>\n(For reference on the sensor's xyz axis orientation, see this <a href=\"https://www.kaggle.com/competitions/cmi-detect-behavior-with-sensor-data/discussion/588501#3243561\" target=\"_blank\">discussion thread</a>).</p>\n<table>\n<thead>\n<tr>\n<th>Normal (Original)</th>\n<th>180° Rotation around Y-axis</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F13641953%2F761af7428925fdc6144ee1c66ea71c6a%2Fnormal.jpg?generation=1756997880870680&amp;alt=media\" alt=\"\"></td>\n<td><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F13641953%2F783d2a11e0901a534668f910c8fe99df%2Fy_180.jpg?generation=1756997909957729&amp;alt=media\" alt=\"\"></td>\n</tr>\n<tr>\n<td>180° Rotation around Z-axis</td>\n<td>180° Y-axis + 180° Z-axis</td>\n</tr>\n<tr>\n<td><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F13641953%2Fcb100f0dfd78c0d775c6d05ed0d784e8%2Fz_180.jpg?generation=1756997918874301&amp;alt=media\" alt=\"\"></td>\n<td><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F13641953%2F26926ea1c2c6b6ca34ef95a60be7945a%2Fy_180_z_180.jpg?generation=1756997926139663&amp;alt=media\" alt=\"\"></td>\n</tr>\n</tbody>\n</table>\n<p>Considering the possibility that test data might also contain similar installation errors, we implemented a binary classification system to detect installation anomalies and apply correction during inference. However, since the number of relevant cases was limited in public testing, the effect was difficult to discern. Therefore, for this submission, we prioritized false positive reduction by setting the threshold to 0.9, resulting in only limited effectiveness. When lowering the threshold to 0.5 in the Late Submission, our score improved to 0.862 😢</p>\n<h2>1.3. DA+TTA with Y-Axis Rotation</h2>\n<p>Building on the assumption of fixed sensor orientation, we addressed the scenario where \"even with proper installation, minor rotation occurs around the y-axis.\" Therefore, we implemented:</p>\n<ul>\n<li>During training: DA with random rotation around the y-axis using a uniform distribution between -15° and +15°</li>\n<li>During inference: TTA that attempts multiple small rotation angles around the y-axis and averages the results</li>\n</ul>\n<p>By setting the inference angles to <code>[0, +5, -5, +15, -15]</code>, we observed an average improvement of approximately +0.002 in public LB across multiple experiments. Private LB also showed similar performance gains.</p>\n<p>For this implementation, we carefully applied the principle that \"rotation operations should be applied from the left to acceleration data and from the right to the rotation data.\"</p>\n<h2>1.4. Creating Soft Labels</h2>\n<p>This section was collaboratively developed with <a href=\"https://www.kaggle.com/mckmckmck\" target=\"_blank\">@mckmckmck</a>.</p>\n<p>While previous optimizations focused on \"preventing difficult subjects from being overlooked,\" when we examined OOF scores by gesture type, we identified certain gestures that were particularly challenging to predict. Here is an example from the IMU-only model:</p>\n<pre><code>-----------------------------------------------------\nF1 score  class[0] = 0.666 =417, =197, =221\nF1 score  class[1] = 0.643 =405, =214, =235\nF1 score  class[2] = 0.824 =548, =142, =92\nF1 score  class[3] = 0.442 =286, =369, =352\nF1 score  class[4] = 0.609 =384, =238, =256\nF1 score  class[5] = 0.538 =340, =284, =300\nF1 score  class[6] = 0.693 =444, =198, =196\nF1 score  class[7] = 0.517 =328, =305, =309\nF1 score  class[8] = 0.985 =2998, =54, =40\n-----------------------------------------------------\nMacro F1 = 0.6573605192818119\nBFRB Macro F1 = 0.6164599766386722\nScore = (Binary F1 + Macro F1) / 2 = 0.824077831125434\n</code></pre>\n<p>※ Class 8 represents non-BFRB gestures. Classes 0-7 correspond to:</p>\n<pre><code>: Above ear - pull hair\n: Forehead - pull hairline\n: Forehead - scratch\n: Eyebrow - pull hair\n: Eyelash - pull hair\n: Neck - pinch skin\n: Neck - scratch\n: Cheek - pinch skin\n</code></pre>\n<p>Furthermore, we visualized the confusion matrix for the BFRB category (where element (i, j) represents the number of sequences with true label i and predicted label j):</p>\n<p><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F13641953%2F12ee9d467f73ad3ebf4d305b43340ae1%2Fimu011_bfrb.png?generation=1757038524229493&amp;alt=media\" alt=\"\"></p>\n<p>From these observations, we identified:</p>\n<ul>\n<li>The distinction between \"Eyebrow - pull hair\" and \"Eyelash - pull hair\" proves particularly difficult</li>\n<li>Other similar gesture categories exist</li>\n</ul>\n<p>We then introduced soft labeling after one-hot encoding, assigning small weights to similar gesture categories, which yielded modest but noticeable improvements overall.</p>\n<h2>1.5. Unsuccessful Approaches and Additional Considerations</h2>\n<p><strong>Areas for Improvement</strong></p>\n<ul>\n<li><p><strong>Stacking</strong></p>\n<ul>\n<li>The weighted average of CV maximization weights calculated using Optuna was discarded as it resulted in lower public leaderboard scores (private score: 0.860–0.861 😢)</li>\n<li>Attempted stacking with LightGBM but found no improvement in CV performance, so abandoned this approach</li></ul></li>\n<li><p><strong>Alignment Correction</strong></p>\n<ul>\n<li>Due to uncertainty about the model's robustness and to avoid false positives, I increased the threshold, which only produced limited effectiveness (see Section 1.2)</li></ul></li>\n</ul>\n<p><strong>Additional Approaches I Considered</strong></p>\n<ul>\n<li><p><strong>Self-Supervised Learning and Adversarial Training</strong></p>\n<ul>\n<li>Although I wanted to experiment with these methods given the large number of subjects (81), lack of expertise prevented implementation</li></ul></li>\n<li><p><strong>Pre-trained Model Utilization</strong></p>\n<ul>\n<li>Referenced <a href=\"https://www.nature.com/articles/s41746-024-01062-3\" target=\"_blank\">this paper</a> on self-supervised learning for human activity recognition and considered transfer learning, but ran out of time</li></ul></li>\n<li><p><strong>Rotation Correction Around the Y-Axis</strong></p>\n<ul>\n<li>Some data exhibited rotation around the y-axis (likely requiring -90° correction for SUBJ_011323 and +60° for SUBJ_032165)</li>\n<li>Since omitting this correction didn't cause significant accuracy degradation compared to other subjects, I prioritized other tasks</li>\n<li>If I had more time, I would have developed a model to predict y-axis rotation angles and implemented the correction</li></ul></li>\n</ul>\n<p>If anyone has successful implementations or insights regarding these areas, I'd be very grateful for your feedback!!</p>\n<h1>2. <a href=\"https://www.kaggle.com/tokkiwa\" target=\"_blank\">@tokkiwa</a> Part (Deep Learning &amp; Modeling)</h1>\n<h2>TL;DR</h2>\n<ul>\n<li>Channel-wise Convolution really matters (33rd place with single model!)</li>\n<li>3D CNN for TOF</li>\n<li>Multitask Training </li>\n</ul>\n<h2>2.1 Model</h2>\n<p>Our main model consists of three parts; (1) Channel-wise Feature Extractor, (2) BiGRU and (3) MLP Head. <br>\nThe latter two are not that interesting, so let's focus on the Feature Extractor. We first noticed that the channel-independent cnn models in <a href=\"https://www.kaggle.com/code/kaigaokaigao/lb-0-75-imu-only-multibranch-inference\" target=\"_blank\">this notebook</a> performs better than other models. Here is the code snippet; </p>\n<pre><code>        .branches = nn.ModuleList(\n            [\n                nn.Sequential(\n                    MultiScaleConv1d(, , kernel_sizes=[, , ]),\n                    ResidualSEBlock(, , , dropout=),\n                    ResidualSEBlock(, , , dropout=),\n                )\n                 _  (num_channels)\n            ]\n        )\n</code></pre>\n<p>This model prepares cnn pipelines independently for each of the channels. As the sensor data have completely different modalities for each channels, it is reasonable to employ independent kernels to extract rich features. We further rewrite the branches with a grouped convolution, initially for the speed-up. Unexpectedly, combining the channel-independent convolution with inter-channel SE block performs much better than the fully channel-independent processing.</p>\n<p>Additionally, our teammate <a href=\"https://www.kaggle.com/chimaki821\" target=\"_blank\">@chimaki821</a> adopted a 3D-CNN for the TOF features to capture the spatio-temporal relationship of the sensors.</p>\n<pre><code>           .tof_branch = nn.Sequential(\n                nn.Conv3d(tof_sensors,  * tof_sensors, (,,), padding=, groups=tof_sensors, bias=),\n                nn.BatchNorm3d( * tof_sensors), nn.ReLU(), nn.MaxPool3d((,,)),\n                nn.Conv3d( * tof_sensors, tof_out_channels * tof_sensors, (,,), padding=, groups=tof_sensors, bias=),\n                nn.BatchNorm3d(tof_out_channels * tof_sensors), nn.ReLU(),\n                nn.AdaptiveMaxPool3d((, , )),\n            )\n</code></pre>\n<p>The features are then fed to the BiGRU and Attention Pooling, added gaussian noise and finally input to the small MLP head. Please refer to our codes for more details. <br>\nFor the submission, we trained some other variant (e.g. with FFT features added) of this model and other simple CNN model seen in the public notebooks. </p>\n<h2>2.2 Traning</h2>\n<p>We used separated losses for behavior, orientation and gesture and summed them up (originally implemented by <a href=\"https://www.kaggle.com/chimaki821\" target=\"_blank\">@chimaki821</a>). <br>\nSpecifically, cross entropy was applied for orientation and gesture (for gesture, label smoothing = 0.2 was our best practice) and a <code>nn.BCEWithLogitsLoss</code> to behavior. <br>\nWe finally used Cosine Annealing LR, Adam and the following data augments: </p>\n<ul>\n<li>random noise</li>\n<li>random scaling</li>\n<li>artificial drift</li>\n<li>random overwrite</li>\n<li>mixup</li>\n</ul>\n<p>Focal loss, exponential moving average, radam and normalization were implemented but not effective for our models. <br>\nThe result of wandb sweep suggests that normalization of the columns affects badly on our models. The gain by multi-task training is also limited, but we suppose it might have a good effect on ensembling as the method enables the model to learn differently. </p>\n<hr>\n<p>（日本語）</p>\n<h1>1. <a href=\"https://www.kaggle.com/chimaki821\" target=\"_blank\">@chimaki821</a> Part (Observation + Preprocess)</h1>\n<h2>1.1. 概要</h2>\n<p>多くの参加者と同様に、以下を実施しました。</p>\n<ul>\n<li>左手データの補正（<code>acc_y</code>・<code>rot_x</code>・<code>rot_y</code> の反転、THM 3↔5 の swap、ToF 3↔5 の swap、8×8 正方形領域の反転）</li>\n<li>マルチタスク学習（<code>orientation</code> と <code>behavior</code> を補助ロスに追加）</li>\n<li>IMU のみ／全センサーでのモデル切替</li>\n<li>取り付け異常が疑われる被験者の補正（部分的に成功、後述）</li>\n<li>重み付きアンサンブル（最終提出には未採用だが private では有効）</li>\n</ul>\n<p>これらの基本実装ののち、チームメイトの <a href=\"https://www.kaggle.com/tokkiwa\" target=\"_blank\">@tokkiwa</a> がモデル改善・パイプライン整備・複数モデルのアンサンブルを進めてくれたことで、スコアが大幅に向上しました。私は機械学習について何も知らなかったため、大変助かりました。</p>\n<p>以下では、上記の方針検討に役立った取り組みと、それ以外に行った</p>\n<ul>\n<li>データ観察と取り付け補正</li>\n<li>y 軸回り回転による DA（Data Augmentation）＋ TTA（Test-Time Augmentation）</li>\n<li>ソフトラベルの作成</li>\n</ul>\n<p>について述べます。あわせて、</p>\n<ul>\n<li>コンペ期間中に試したがうまくいかなかったこと・やりたかったこと</li>\n</ul>\n<p>もまとめます。</p>\n<h2>1.2. データ観察と取り付け補正</h2>\n<p>このパートは <a href=\"https://www.kaggle.com/epopaca\" target=\"_blank\">@epopaca</a> と共同で進めました。</p>\n<p>subject と gesture を指定すると該当データを 1 枚の画像にまとめて出力する <a href=\"https://www.kaggle.com/code/chimaki821/visualization-for-19th-place-solution\" target=\"_blank\">notebook</a> を作成しました。81人×18 gestures = 1,458 枚の画像をスマートフォンに保存し、時間があれば常にこれを観察していました。</p>\n<p>（出力例は英語版をご覧ください。）</p>\n<p>この可視化を使いながら、左手補正や取り付け補正を検討しました。<code>acc_x, acc_y, acc_z</code> を見ると、<code>SUBJ_019262</code> と <code>SUBJ_045235</code> は取り付けが不適切だと推測できます。Herios sensor の取り付けミスを想定すると（正常を含め）大きく4通りが考えられます（センサーの xyz 軸の向きは <a href=\"https://www.kaggle.com/competitions/cmi-detect-behavior-with-sensor-data/discussion/588501#3243561\" target=\"_blank\">この discussion</a> を参照）。</p>\n<p>（図は英語版をご覧ください。）</p>\n<p>テスト側にも同様の取り付け方ミスがある可能性を考え、2 値分類で取り付け異常を判定し、推論時に補正をかける仕組みを用意しました。ところが public では該当ケースが少なく効果が見えづらかったため、本提出では偽陽性抑制を優先して閾値を 0.9 に設定。その結果、効果は限定的でした。Late Submission で閾値を 0.5 に下げたところ、スコアが 0.862 まで上昇しました 😢</p>\n<h2>1.3. y 軸回り回転による DA＋TTA</h2>\n<p>センサー軸が固定されている前提から、「正常取り付けのつもりでも y 軸回りにわずかな回転が入る」ケースを想定しました。そこで、</p>\n<ul>\n<li>学習時：y 軸回りに一様乱数（−15°〜＋15°）で回転させる DA</li>\n<li>推論時：y 軸回りの微小回転を複数角度で試し平均化する TTA</li>\n</ul>\n<p>を実装。推論角度を <code>[0, +5, -5, +15, -15]</code> としたところ、複数の実験で public LB が平均で約 +0.002 改善。private でも同程度の上昇を確認しました。</p>\n<p>この実装を行うのにあたって、「加速度データには回転作用素を左から作用、回転データには回転作用素の逆作用素を右から作用」させることに注意しました。</p>\n<h2>1.4. ソフトラベルの作成</h2>\n<p>このパートは <a href=\"https://www.kaggle.com/mckmckmck\" target=\"_blank\">@mckmckmck</a> と共同で行いました。</p>\n<p>これまでの工夫は「難しい被験者を取りこぼさない」ことを目指したものでしたが、OOF（out-of-fold）について gesture 別にスコアを求めてみると、予測が難しい gesture があることがわかりました。IMU Only モデルの例：</p>\n<pre><code>-----------------------------------------------------\nF1 of class[0] = 0.666 =417, =197, =221\nF1 of class[1] = 0.643 =405, =214, =235\nF1 of class[2] = 0.824 =548, =142, =92\nF1 of class[3] = 0.442 =286, =369, =352\nF1 of class[4] = 0.609 =384, =238, =256\nF1 of class[5] = 0.538 =340, =284, =300\nF1 of class[6] = 0.693 =444, =198, =196\nF1 of class[7] = 0.517 =328, =305, =309\nF1 of class[8] = 0.985 =2998, =54, =40\n-----------------------------------------------------\nMacro F1 = 0.6573605192818119\nBFRB Macro F1 = 0.6164599766386722\nScore = (BinaryF1 + Macro F1) / 2 = 0.824077831125434\n</code></pre>\n<p>※ class 8 は non-BFRB。class 0〜7 は以下：</p>\n<pre><code>: Above ear - pull hair\n: Forehead - pull hairline\n: Forehead - scratch\n: Eyebrow - pull hair\n: Eyelash - pull hair\n: Neck - pinch skin\n: Neck - scratch\n: Cheek - pinch skin\n</code></pre>\n<p>さらに、BFRB 部分の混同行列を可視化（(i, j) 成分は 正解が i 番目の gesture, 予測が j 番目の gesture となる sequence の数）</p>\n<p>（図は英語版をご覧ください）</p>\n<p>これらの観察から、</p>\n<ul>\n<li>“Eyebrow - pull hair” と “Eyelash - pull hair” は類似</li>\n<li>その他にも類似 gesture が存在</li>\n</ul>\n<p>ことが分かります。そこで one-hot の後に、類似 gesture に小さな重みを配分するソフトラベリングを導入したところ、全体的にわずかながら改善が得られました。</p>\n<h2>1.5. うまくいかなかったこと／他にやりたかったこと</h2>\n<p><strong>うまくいかなかったこと</strong></p>\n<ul>\n<li><p><strong>Stacking</strong></p>\n<ul>\n<li>Optuna で CV 最大化の重みを求めた重み付き平均は、public LB が低下したため選択せず（private score: 0.860〜0.861 😢）</li>\n<li>LightGBM による stacking は CV 改善が見られず断念</li></ul></li>\n<li><p><strong>取り付け補正</strong></p>\n<ul>\n<li>モデルの強さに確信が持てず偽陽性を避けるため閾値を上げた結果、効果が限定的（§1.2 参照）</li></ul></li>\n</ul>\n<p><strong>他にやりたかったこと</strong></p>\n<ul>\n<li><p><strong>自己教師あり学習・対照学習</strong></p>\n<ul>\n<li>subject が 81 と多くないため試したかったが、知識不足で着手できず</li></ul></li>\n<li><p><strong>事前学習モデルの利用</strong></p>\n<ul>\n<li><a href=\"https://www.nature.com/articles/s41746-024-01062-3\" target=\"_blank\">Self-supervised learning for human activity recognition using 700,000 person-days of wearable data</a> を参照し、転移学習を検討したが時間不足</li></ul></li>\n<li><p><strong>y 軸回り回転の回帰補正</strong></p>\n<ul>\n<li>y 軸周りに回転してしまっているデータもある（おそらく SUBJ_011323 は -90度, SUBJ_032165 は +60度の補正が必要）</li>\n<li>やらなくても他の subject と比べて大幅な精度低下にはなっていなかったため優先しなかった</li>\n<li>余裕があったら y 軸周りの回転角度を予測するモデルを作り、補正したかった</li></ul></li>\n</ul>\n<p>これらについて成功事例・検討事例があれば、ぜひ教えてください！！</p>\n<h1>2. <a href=\"https://www.kaggle.com/tokkiwa\" target=\"_blank\">@tokkiwa</a> part</h1>\n<h2>TL;DR</h2>\n<ul>\n<li>グループ化畳み込みが有効 (1モデルで33位相当) </li>\n<li>TOF に 3D CNN </li>\n<li>マルチタスク学習</li>\n</ul>\n<h2>2.1 Model</h2>\n<p>自分たちのモデルは、(1)特徴量抽出、(2)BiGRU と (3) MLPの予測ヘッドからなります。後者二つはPublicにあるようなものなので、おもに、主に特徴抽出について説明します。<br>\nはじめに、<a href=\"https://www.kaggle.com/code/kaigaokaigao/lb-0-75-imu-only-multibranch-inference\" target=\"_blank\">この notebook</a> にあるチャンネル独立なモデルの性能が良いことに気づきました。</p>\n<p>このモデルは次のように、各チャンネルについて完全に独立したCNNを用意して、各チャンネルを48倍に拡大しながら畳み込み処理をおこないます。</p>\n<pre><code>        .branches = nn.ModuleList(\n            [\n                nn.Sequential(\n                    MultiScaleConv1d(, , kernel_sizes=[, , ]),\n                    ResidualSEBlock(, , , dropout=),\n                    ResidualSEBlock(, , , dropout=),\n                )\n                 _  (num_channels)\n            ]\n        )\n</code></pre>\n<p>今回のセンサーデータはそれぞれ全く違うモダリティがあるので、はじめに混ぜながら処理するよりも、独立に特徴量を得る方がリッチな特徴量を得られるためだと思います。<br>\nもともとのモデルはfor文でチャンネルごとに処理をしていたので、グループ化畳み込みで高速化を行いました。この途中でわかったこととして、SEブロックのみは全チャンネルで作用させるとより性能が向上します。<br>\nしたがって、最終的にIMUとTHM特徴量についてはチャンネル毎畳み込みで処理し、途中のSEブロックのみ全チャンネルで処理するというパイプラインになりました。</p>\n<p>TOF特徴量については空間的にセンサーが並んでいるので、3D-CNNを用いて空間方向と系列方向を別に扱うような形にしました。 ( <a href=\"https://www.kaggle.com/chimaki821\" target=\"_blank\">@chimaki821</a> のアイデアです)</p>\n<pre><code>           .tof_branch = nn.Sequential(\n                nn.Conv3d(tof_sensors,  * tof_sensors, (,,), padding=, groups=tof_sensors, bias=),\n                nn.BatchNorm3d( * tof_sensors), nn.ReLU(), nn.MaxPool3d((,,)),\n                nn.Conv3d( * tof_sensors, tof_out_channels * tof_sensors, (,,), padding=, groups=tof_sensors, bias=),\n                nn.BatchNorm3d(tof_out_channels * tof_sensors), nn.ReLU(),\n                nn.AdaptiveMaxPool3d((, , )),\n            )\n</code></pre>\n<p>特徴量はその後にBiGRUとAttention Pooling に入力され、ガウスノイズを掛けてMLPヘッドに入力され、推論結果が出力されます。詳しくはコードを参照してください。<br>\nアンサンブルについては、このモデルの変種 (FFT特徴量を入れたものなど)と、publicに見られたようなシンプルなCNNモデルを混ぜました。先述した回転のpostprocessing などを掛ける都合で、モデルは4~5個程度しか使えなかったので、あまり多くのモデルは使っていません。</p>\n<h2>2.2 訓練</h2>\n<p>behavior, orientation, gesture にそれぞれ異なるロスを設定するマルチタスク学習を行いました。( <a href=\"https://www.kaggle.com/chimaki821\" target=\"_blank\">@chimaki821</a>  が実装してくれました) gesture,  orientation に対してはCross Entropy (gestureのみlabel_smoothing = 0.2), behavior については <code>nn.BCEWithLogitsLoss</code> を用いて、すべてのロスを足し合わせたものを用いました。</p>\n<p>最終的に、訓練には Cosine Annealing LR, Adam と、次のデータ拡張を用いました。</p>\n<ul>\n<li>random noise</li>\n<li>random scaling</li>\n<li>drift (蓄積する時間ノイズの再現)</li>\n<li>random overwrite</li>\n<li>mixup</li>\n</ul>\n<p>Focal loss, 移動指数平均 (EMA), radam と正規化を行いましたが、自分たちのモデルには効果がありませんでした。特に、Wandbでパラメータチューニングを行った限りだと、正規化はしないほうが明らかにパフォーマンスが向上します。マルチタスク学習についてはCVへの寄与度はあまりありませんでしたが、アンサンブルにあたっては多様な特徴量が取れてよいのではないかと思います。</p>",
  "messages": [
    {
      "id": 3281462,
      "postDate": "2025-09-04T10:46:12.880Z",
      "content": "<p>（下に日本語による説明を書いています）</p>\n<p>We would like to express our deepest gratitude to the competition hosts! We really enjoyed the competition for the entire period. <br>\nThis write-up is separated in two parts: the observation, preprocessing and postprocessing by <a href=\"https://www.kaggle.com/chimaki821\" target=\"_blank\">@chimaki821</a> then modeling, deep leaning by <a href=\"https://www.kaggle.com/tokkiwa\" target=\"_blank\">@tokkiwa</a>. </p>\n<h1>1. <a href=\"https://www.kaggle.com/chimaki821\" target=\"_blank\">@chimaki821</a> Part (Observation + Preprocessing)</h1>\n<p>I created this discussion using <a href=\"https://translate-demo.plamo.preferredai.jp/\" target=\"_blank\">PLaMo Translations</a>.</p>\n<h2>1.1. Overview</h2>\n<p>Like most participants, I implemented the following techniques:</p>\n<ul>\n<li>Corrected left-handed data by inverting <code>acc_y</code>, <code>rot_x</code>, and <code>rot_y</code> values, swapping THM channels 3↔5, swapping ToF channels 3↔5, and inverting an 8×8 square region</li>\n<li>Implemented multitask learning by adding <code>orientation</code> and <code>behavior</code> as auxiliary loss terms</li>\n<li>Switched between IMU-only and full sensor model configurations</li>\n<li>Applied corrections to subjects suspected of having mounting anomalies (partially successful, details follow)</li>\n<li>Used weighted ensemble methods (though not included in the final submission, these proved effective in private testing)</li>\n</ul>\n<p>After implementing these baseline approaches, my teammate <a href=\"https://www.kaggle.com/tokkiwa\" target=\"_blank\">@tokkiwa</a> significantly improved the model through various enhancements to the pipeline and ensemble methods with multiple models. This proved extremely valuable since I had virtually no background in machine learning.</p>\n<p>Below, I describe several key techniques that helped inform the above approach, as well as additional methods I implemented:</p>\n<ul>\n<li>Data observation and mounting correction</li>\n<li>Data augmentation (DA) and test-time augmentation (TTA) using rotation around the y-axis</li>\n<li>Creation of soft labels</li>\n</ul>\n<p>I also summarize:</p>\n<ul>\n<li>What I tried during the competition that didn't work or was left unimplemented</li>\n<li>Additional approaches I wanted to explore but couldn't implement in time</li>\n</ul>\n<h2>1.2. Data Observation and Mounting Correction</h2>\n<p>This section was developed in collaboration with <a href=\"https://www.kaggle.com/epopaca\" target=\"_blank\">@epopaca</a>.</p>\n<p>We created a <a href=\"https://www.kaggle.com/code/chimaki821/visualization-for-19th-place-solution\" target=\"_blank\">notebook</a> that, when provided with a specific subject and gesture, consolidates all relevant data into a single image. 81 subjects × 18 gestures = 1,458 images saved on my smartphone, constantly observing them whenever I had time.</p>\n<p>e.g. (SUBJ_000206, gesture: Above Ear - pull hair)<br>\n<img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F13641953%2F7da4b813f511805d152e32eb71f95fd9%2Fsubj_000206_gesture0.png?generation=1757046751796413&amp;alt=media\" alt=\"\"></p>\n<p>Using this visualization, we conducted analyses on left-hand correction and mounting adjustments. By examining <code>acc_x, acc_y, acc_z</code> values, we determined that <code>SUBJ_019262</code> and <code>SUBJ_045235</code> likely had improper mounting. Considering potential issues with the Herios sensor's attachment, there are four main possible scenarios (including normal conditions):<br>\n(For reference on the sensor's xyz axis orientation, see this <a href=\"https://www.kaggle.com/competitions/cmi-detect-behavior-with-sensor-data/discussion/588501#3243561\" target=\"_blank\">discussion thread</a>).</p>\n<table>\n<thead>\n<tr>\n<th>Normal (Original)</th>\n<th>180° Rotation around Y-axis</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F13641953%2F761af7428925fdc6144ee1c66ea71c6a%2Fnormal.jpg?generation=1756997880870680&amp;alt=media\" alt=\"\"></td>\n<td><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F13641953%2F783d2a11e0901a534668f910c8fe99df%2Fy_180.jpg?generation=1756997909957729&amp;alt=media\" alt=\"\"></td>\n</tr>\n<tr>\n<td>180° Rotation around Z-axis</td>\n<td>180° Y-axis + 180° Z-axis</td>\n</tr>\n<tr>\n<td><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F13641953%2Fcb100f0dfd78c0d775c6d05ed0d784e8%2Fz_180.jpg?generation=1756997918874301&amp;alt=media\" alt=\"\"></td>\n<td><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F13641953%2F26926ea1c2c6b6ca34ef95a60be7945a%2Fy_180_z_180.jpg?generation=1756997926139663&amp;alt=media\" alt=\"\"></td>\n</tr>\n</tbody>\n</table>\n<p>Considering the possibility that test data might also contain similar installation errors, we implemented a binary classification system to detect installation anomalies and apply correction during inference. However, since the number of relevant cases was limited in public testing, the effect was difficult to discern. Therefore, for this submission, we prioritized false positive reduction by setting the threshold to 0.9, resulting in only limited effectiveness. When lowering the threshold to 0.5 in the Late Submission, our score improved to 0.862 😢</p>\n<h2>1.3. DA+TTA with Y-Axis Rotation</h2>\n<p>Building on the assumption of fixed sensor orientation, we addressed the scenario where \"even with proper installation, minor rotation occurs around the y-axis.\" Therefore, we implemented:</p>\n<ul>\n<li>During training: DA with random rotation around the y-axis using a uniform distribution between -15° and +15°</li>\n<li>During inference: TTA that attempts multiple small rotation angles around the y-axis and averages the results</li>\n</ul>\n<p>By setting the inference angles to <code>[0, +5, -5, +15, -15]</code>, we observed an average improvement of approximately +0.002 in public LB across multiple experiments. Private LB also showed similar performance gains.</p>\n<p>For this implementation, we carefully applied the principle that \"rotation operations should be applied from the left to acceleration data and from the right to the rotation data.\"</p>\n<h2>1.4. Creating Soft Labels</h2>\n<p>This section was collaboratively developed with <a href=\"https://www.kaggle.com/mckmckmck\" target=\"_blank\">@mckmckmck</a>.</p>\n<p>While previous optimizations focused on \"preventing difficult subjects from being overlooked,\" when we examined OOF scores by gesture type, we identified certain gestures that were particularly challenging to predict. Here is an example from the IMU-only model:</p>\n<pre><code>-----------------------------------------------------\nF1 score  class[0] = 0.666 =417, =197, =221\nF1 score  class[1] = 0.643 =405, =214, =235\nF1 score  class[2] = 0.824 =548, =142, =92\nF1 score  class[3] = 0.442 =286, =369, =352\nF1 score  class[4] = 0.609 =384, =238, =256\nF1 score  class[5] = 0.538 =340, =284, =300\nF1 score  class[6] = 0.693 =444, =198, =196\nF1 score  class[7] = 0.517 =328, =305, =309\nF1 score  class[8] = 0.985 =2998, =54, =40\n-----------------------------------------------------\nMacro F1 = 0.6573605192818119\nBFRB Macro F1 = 0.6164599766386722\nScore = (Binary F1 + Macro F1) / 2 = 0.824077831125434\n</code></pre>\n<p>※ Class 8 represents non-BFRB gestures. Classes 0-7 correspond to:</p>\n<pre><code>: Above ear - pull hair\n: Forehead - pull hairline\n: Forehead - scratch\n: Eyebrow - pull hair\n: Eyelash - pull hair\n: Neck - pinch skin\n: Neck - scratch\n: Cheek - pinch skin\n</code></pre>\n<p>Furthermore, we visualized the confusion matrix for the BFRB category (where element (i, j) represents the number of sequences with true label i and predicted label j):</p>\n<p><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F13641953%2F12ee9d467f73ad3ebf4d305b43340ae1%2Fimu011_bfrb.png?generation=1757038524229493&amp;alt=media\" alt=\"\"></p>\n<p>From these observations, we identified:</p>\n<ul>\n<li>The distinction between \"Eyebrow - pull hair\" and \"Eyelash - pull hair\" proves particularly difficult</li>\n<li>Other similar gesture categories exist</li>\n</ul>\n<p>We then introduced soft labeling after one-hot encoding, assigning small weights to similar gesture categories, which yielded modest but noticeable improvements overall.</p>\n<h2>1.5. Unsuccessful Approaches and Additional Considerations</h2>\n<p><strong>Areas for Improvement</strong></p>\n<ul>\n<li><p><strong>Stacking</strong></p>\n<ul>\n<li>The weighted average of CV maximization weights calculated using Optuna was discarded as it resulted in lower public leaderboard scores (private score: 0.860–0.861 😢)</li>\n<li>Attempted stacking with LightGBM but found no improvement in CV performance, so abandoned this approach</li></ul></li>\n<li><p><strong>Alignment Correction</strong></p>\n<ul>\n<li>Due to uncertainty about the model's robustness and to avoid false positives, I increased the threshold, which only produced limited effectiveness (see Section 1.2)</li></ul></li>\n</ul>\n<p><strong>Additional Approaches I Considered</strong></p>\n<ul>\n<li><p><strong>Self-Supervised Learning and Adversarial Training</strong></p>\n<ul>\n<li>Although I wanted to experiment with these methods given the large number of subjects (81), lack of expertise prevented implementation</li></ul></li>\n<li><p><strong>Pre-trained Model Utilization</strong></p>\n<ul>\n<li>Referenced <a href=\"https://www.nature.com/articles/s41746-024-01062-3\" target=\"_blank\">this paper</a> on self-supervised learning for human activity recognition and considered transfer learning, but ran out of time</li></ul></li>\n<li><p><strong>Rotation Correction Around the Y-Axis</strong></p>\n<ul>\n<li>Some data exhibited rotation around the y-axis (likely requiring -90° correction for SUBJ_011323 and +60° for SUBJ_032165)</li>\n<li>Since omitting this correction didn't cause significant accuracy degradation compared to other subjects, I prioritized other tasks</li>\n<li>If I had more time, I would have developed a model to predict y-axis rotation angles and implemented the correction</li></ul></li>\n</ul>\n<p>If anyone has successful implementations or insights regarding these areas, I'd be very grateful for your feedback!!</p>\n<h1>2. <a href=\"https://www.kaggle.com/tokkiwa\" target=\"_blank\">@tokkiwa</a> Part (Deep Learning &amp; Modeling)</h1>\n<h2>TL;DR</h2>\n<ul>\n<li>Channel-wise Convolution really matters (33rd place with single model!)</li>\n<li>3D CNN for TOF</li>\n<li>Multitask Training </li>\n</ul>\n<h2>2.1 Model</h2>\n<p>Our main model consists of three parts; (1) Channel-wise Feature Extractor, (2) BiGRU and (3) MLP Head. <br>\nThe latter two are not that interesting, so let's focus on the Feature Extractor. We first noticed that the channel-independent cnn models in <a href=\"https://www.kaggle.com/code/kaigaokaigao/lb-0-75-imu-only-multibranch-inference\" target=\"_blank\">this notebook</a> performs better than other models. Here is the code snippet; </p>\n<pre><code>        .branches = nn.ModuleList(\n            [\n                nn.Sequential(\n                    MultiScaleConv1d(, , kernel_sizes=[, , ]),\n                    ResidualSEBlock(, , , dropout=),\n                    ResidualSEBlock(, , , dropout=),\n                )\n                 _  (num_channels)\n            ]\n        )\n</code></pre>\n<p>This model prepares cnn pipelines independently for each of the channels. As the sensor data have completely different modalities for each channels, it is reasonable to employ independent kernels to extract rich features. We further rewrite the branches with a grouped convolution, initially for the speed-up. Unexpectedly, combining the channel-independent convolution with inter-channel SE block performs much better than the fully channel-independent processing.</p>\n<p>Additionally, our teammate <a href=\"https://www.kaggle.com/chimaki821\" target=\"_blank\">@chimaki821</a> adopted a 3D-CNN for the TOF features to capture the spatio-temporal relationship of the sensors.</p>\n<pre><code>           .tof_branch = nn.Sequential(\n                nn.Conv3d(tof_sensors,  * tof_sensors, (,,), padding=, groups=tof_sensors, bias=),\n                nn.BatchNorm3d( * tof_sensors), nn.ReLU(), nn.MaxPool3d((,,)),\n                nn.Conv3d( * tof_sensors, tof_out_channels * tof_sensors, (,,), padding=, groups=tof_sensors, bias=),\n                nn.BatchNorm3d(tof_out_channels * tof_sensors), nn.ReLU(),\n                nn.AdaptiveMaxPool3d((, , )),\n            )\n</code></pre>\n<p>The features are then fed to the BiGRU and Attention Pooling, added gaussian noise and finally input to the small MLP head. Please refer to our codes for more details. <br>\nFor the submission, we trained some other variant (e.g. with FFT features added) of this model and other simple CNN model seen in the public notebooks. </p>\n<h2>2.2 Traning</h2>\n<p>We used separated losses for behavior, orientation and gesture and summed them up (originally implemented by <a href=\"https://www.kaggle.com/chimaki821\" target=\"_blank\">@chimaki821</a>). <br>\nSpecifically, cross entropy was applied for orientation and gesture (for gesture, label smoothing = 0.2 was our best practice) and a <code>nn.BCEWithLogitsLoss</code> to behavior. <br>\nWe finally used Cosine Annealing LR, Adam and the following data augments: </p>\n<ul>\n<li>random noise</li>\n<li>random scaling</li>\n<li>artificial drift</li>\n<li>random overwrite</li>\n<li>mixup</li>\n</ul>\n<p>Focal loss, exponential moving average, radam and normalization were implemented but not effective for our models. <br>\nThe result of wandb sweep suggests that normalization of the columns affects badly on our models. The gain by multi-task training is also limited, but we suppose it might have a good effect on ensembling as the method enables the model to learn differently. </p>\n<hr>\n<p>（日本語）</p>\n<h1>1. <a href=\"https://www.kaggle.com/chimaki821\" target=\"_blank\">@chimaki821</a> Part (Observation + Preprocess)</h1>\n<h2>1.1. 概要</h2>\n<p>多くの参加者と同様に、以下を実施しました。</p>\n<ul>\n<li>左手データの補正（<code>acc_y</code>・<code>rot_x</code>・<code>rot_y</code> の反転、THM 3↔5 の swap、ToF 3↔5 の swap、8×8 正方形領域の反転）</li>\n<li>マルチタスク学習（<code>orientation</code> と <code>behavior</code> を補助ロスに追加）</li>\n<li>IMU のみ／全センサーでのモデル切替</li>\n<li>取り付け異常が疑われる被験者の補正（部分的に成功、後述）</li>\n<li>重み付きアンサンブル（最終提出には未採用だが private では有効）</li>\n</ul>\n<p>これらの基本実装ののち、チームメイトの <a href=\"https://www.kaggle.com/tokkiwa\" target=\"_blank\">@tokkiwa</a> がモデル改善・パイプライン整備・複数モデルのアンサンブルを進めてくれたことで、スコアが大幅に向上しました。私は機械学習について何も知らなかったため、大変助かりました。</p>\n<p>以下では、上記の方針検討に役立った取り組みと、それ以外に行った</p>\n<ul>\n<li>データ観察と取り付け補正</li>\n<li>y 軸回り回転による DA（Data Augmentation）＋ TTA（Test-Time Augmentation）</li>\n<li>ソフトラベルの作成</li>\n</ul>\n<p>について述べます。あわせて、</p>\n<ul>\n<li>コンペ期間中に試したがうまくいかなかったこと・やりたかったこと</li>\n</ul>\n<p>もまとめます。</p>\n<h2>1.2. データ観察と取り付け補正</h2>\n<p>このパートは <a href=\"https://www.kaggle.com/epopaca\" target=\"_blank\">@epopaca</a> と共同で進めました。</p>\n<p>subject と gesture を指定すると該当データを 1 枚の画像にまとめて出力する <a href=\"https://www.kaggle.com/code/chimaki821/visualization-for-19th-place-solution\" target=\"_blank\">notebook</a> を作成しました。81人×18 gestures = 1,458 枚の画像をスマートフォンに保存し、時間があれば常にこれを観察していました。</p>\n<p>（出力例は英語版をご覧ください。）</p>\n<p>この可視化を使いながら、左手補正や取り付け補正を検討しました。<code>acc_x, acc_y, acc_z</code> を見ると、<code>SUBJ_019262</code> と <code>SUBJ_045235</code> は取り付けが不適切だと推測できます。Herios sensor の取り付けミスを想定すると（正常を含め）大きく4通りが考えられます（センサーの xyz 軸の向きは <a href=\"https://www.kaggle.com/competitions/cmi-detect-behavior-with-sensor-data/discussion/588501#3243561\" target=\"_blank\">この discussion</a> を参照）。</p>\n<p>（図は英語版をご覧ください。）</p>\n<p>テスト側にも同様の取り付け方ミスがある可能性を考え、2 値分類で取り付け異常を判定し、推論時に補正をかける仕組みを用意しました。ところが public では該当ケースが少なく効果が見えづらかったため、本提出では偽陽性抑制を優先して閾値を 0.9 に設定。その結果、効果は限定的でした。Late Submission で閾値を 0.5 に下げたところ、スコアが 0.862 まで上昇しました 😢</p>\n<h2>1.3. y 軸回り回転による DA＋TTA</h2>\n<p>センサー軸が固定されている前提から、「正常取り付けのつもりでも y 軸回りにわずかな回転が入る」ケースを想定しました。そこで、</p>\n<ul>\n<li>学習時：y 軸回りに一様乱数（−15°〜＋15°）で回転させる DA</li>\n<li>推論時：y 軸回りの微小回転を複数角度で試し平均化する TTA</li>\n</ul>\n<p>を実装。推論角度を <code>[0, +5, -5, +15, -15]</code> としたところ、複数の実験で public LB が平均で約 +0.002 改善。private でも同程度の上昇を確認しました。</p>\n<p>この実装を行うのにあたって、「加速度データには回転作用素を左から作用、回転データには回転作用素の逆作用素を右から作用」させることに注意しました。</p>\n<h2>1.4. ソフトラベルの作成</h2>\n<p>このパートは <a href=\"https://www.kaggle.com/mckmckmck\" target=\"_blank\">@mckmckmck</a> と共同で行いました。</p>\n<p>これまでの工夫は「難しい被験者を取りこぼさない」ことを目指したものでしたが、OOF（out-of-fold）について gesture 別にスコアを求めてみると、予測が難しい gesture があることがわかりました。IMU Only モデルの例：</p>\n<pre><code>-----------------------------------------------------\nF1 of class[0] = 0.666 =417, =197, =221\nF1 of class[1] = 0.643 =405, =214, =235\nF1 of class[2] = 0.824 =548, =142, =92\nF1 of class[3] = 0.442 =286, =369, =352\nF1 of class[4] = 0.609 =384, =238, =256\nF1 of class[5] = 0.538 =340, =284, =300\nF1 of class[6] = 0.693 =444, =198, =196\nF1 of class[7] = 0.517 =328, =305, =309\nF1 of class[8] = 0.985 =2998, =54, =40\n-----------------------------------------------------\nMacro F1 = 0.6573605192818119\nBFRB Macro F1 = 0.6164599766386722\nScore = (BinaryF1 + Macro F1) / 2 = 0.824077831125434\n</code></pre>\n<p>※ class 8 は non-BFRB。class 0〜7 は以下：</p>\n<pre><code>: Above ear - pull hair\n: Forehead - pull hairline\n: Forehead - scratch\n: Eyebrow - pull hair\n: Eyelash - pull hair\n: Neck - pinch skin\n: Neck - scratch\n: Cheek - pinch skin\n</code></pre>\n<p>さらに、BFRB 部分の混同行列を可視化（(i, j) 成分は 正解が i 番目の gesture, 予測が j 番目の gesture となる sequence の数）</p>\n<p>（図は英語版をご覧ください）</p>\n<p>これらの観察から、</p>\n<ul>\n<li>“Eyebrow - pull hair” と “Eyelash - pull hair” は類似</li>\n<li>その他にも類似 gesture が存在</li>\n</ul>\n<p>ことが分かります。そこで one-hot の後に、類似 gesture に小さな重みを配分するソフトラベリングを導入したところ、全体的にわずかながら改善が得られました。</p>\n<h2>1.5. うまくいかなかったこと／他にやりたかったこと</h2>\n<p><strong>うまくいかなかったこと</strong></p>\n<ul>\n<li><p><strong>Stacking</strong></p>\n<ul>\n<li>Optuna で CV 最大化の重みを求めた重み付き平均は、public LB が低下したため選択せず（private score: 0.860〜0.861 😢）</li>\n<li>LightGBM による stacking は CV 改善が見られず断念</li></ul></li>\n<li><p><strong>取り付け補正</strong></p>\n<ul>\n<li>モデルの強さに確信が持てず偽陽性を避けるため閾値を上げた結果、効果が限定的（§1.2 参照）</li></ul></li>\n</ul>\n<p><strong>他にやりたかったこと</strong></p>\n<ul>\n<li><p><strong>自己教師あり学習・対照学習</strong></p>\n<ul>\n<li>subject が 81 と多くないため試したかったが、知識不足で着手できず</li></ul></li>\n<li><p><strong>事前学習モデルの利用</strong></p>\n<ul>\n<li><a href=\"https://www.nature.com/articles/s41746-024-01062-3\" target=\"_blank\">Self-supervised learning for human activity recognition using 700,000 person-days of wearable data</a> を参照し、転移学習を検討したが時間不足</li></ul></li>\n<li><p><strong>y 軸回り回転の回帰補正</strong></p>\n<ul>\n<li>y 軸周りに回転してしまっているデータもある（おそらく SUBJ_011323 は -90度, SUBJ_032165 は +60度の補正が必要）</li>\n<li>やらなくても他の subject と比べて大幅な精度低下にはなっていなかったため優先しなかった</li>\n<li>余裕があったら y 軸周りの回転角度を予測するモデルを作り、補正したかった</li></ul></li>\n</ul>\n<p>これらについて成功事例・検討事例があれば、ぜひ教えてください！！</p>\n<h1>2. <a href=\"https://www.kaggle.com/tokkiwa\" target=\"_blank\">@tokkiwa</a> part</h1>\n<h2>TL;DR</h2>\n<ul>\n<li>グループ化畳み込みが有効 (1モデルで33位相当) </li>\n<li>TOF に 3D CNN </li>\n<li>マルチタスク学習</li>\n</ul>\n<h2>2.1 Model</h2>\n<p>自分たちのモデルは、(1)特徴量抽出、(2)BiGRU と (3) MLPの予測ヘッドからなります。後者二つはPublicにあるようなものなので、おもに、主に特徴抽出について説明します。<br>\nはじめに、<a href=\"https://www.kaggle.com/code/kaigaokaigao/lb-0-75-imu-only-multibranch-inference\" target=\"_blank\">この notebook</a> にあるチャンネル独立なモデルの性能が良いことに気づきました。</p>\n<p>このモデルは次のように、各チャンネルについて完全に独立したCNNを用意して、各チャンネルを48倍に拡大しながら畳み込み処理をおこないます。</p>\n<pre><code>        .branches = nn.ModuleList(\n            [\n                nn.Sequential(\n                    MultiScaleConv1d(, , kernel_sizes=[, , ]),\n                    ResidualSEBlock(, , , dropout=),\n                    ResidualSEBlock(, , , dropout=),\n                )\n                 _  (num_channels)\n            ]\n        )\n</code></pre>\n<p>今回のセンサーデータはそれぞれ全く違うモダリティがあるので、はじめに混ぜながら処理するよりも、独立に特徴量を得る方がリッチな特徴量を得られるためだと思います。<br>\nもともとのモデルはfor文でチャンネルごとに処理をしていたので、グループ化畳み込みで高速化を行いました。この途中でわかったこととして、SEブロックのみは全チャンネルで作用させるとより性能が向上します。<br>\nしたがって、最終的にIMUとTHM特徴量についてはチャンネル毎畳み込みで処理し、途中のSEブロックのみ全チャンネルで処理するというパイプラインになりました。</p>\n<p>TOF特徴量については空間的にセンサーが並んでいるので、3D-CNNを用いて空間方向と系列方向を別に扱うような形にしました。 ( <a href=\"https://www.kaggle.com/chimaki821\" target=\"_blank\">@chimaki821</a> のアイデアです)</p>\n<pre><code>           .tof_branch = nn.Sequential(\n                nn.Conv3d(tof_sensors,  * tof_sensors, (,,), padding=, groups=tof_sensors, bias=),\n                nn.BatchNorm3d( * tof_sensors), nn.ReLU(), nn.MaxPool3d((,,)),\n                nn.Conv3d( * tof_sensors, tof_out_channels * tof_sensors, (,,), padding=, groups=tof_sensors, bias=),\n                nn.BatchNorm3d(tof_out_channels * tof_sensors), nn.ReLU(),\n                nn.AdaptiveMaxPool3d((, , )),\n            )\n</code></pre>\n<p>特徴量はその後にBiGRUとAttention Pooling に入力され、ガウスノイズを掛けてMLPヘッドに入力され、推論結果が出力されます。詳しくはコードを参照してください。<br>\nアンサンブルについては、このモデルの変種 (FFT特徴量を入れたものなど)と、publicに見られたようなシンプルなCNNモデルを混ぜました。先述した回転のpostprocessing などを掛ける都合で、モデルは4~5個程度しか使えなかったので、あまり多くのモデルは使っていません。</p>\n<h2>2.2 訓練</h2>\n<p>behavior, orientation, gesture にそれぞれ異なるロスを設定するマルチタスク学習を行いました。( <a href=\"https://www.kaggle.com/chimaki821\" target=\"_blank\">@chimaki821</a>  が実装してくれました) gesture,  orientation に対してはCross Entropy (gestureのみlabel_smoothing = 0.2), behavior については <code>nn.BCEWithLogitsLoss</code> を用いて、すべてのロスを足し合わせたものを用いました。</p>\n<p>最終的に、訓練には Cosine Annealing LR, Adam と、次のデータ拡張を用いました。</p>\n<ul>\n<li>random noise</li>\n<li>random scaling</li>\n<li>drift (蓄積する時間ノイズの再現)</li>\n<li>random overwrite</li>\n<li>mixup</li>\n</ul>\n<p>Focal loss, 移動指数平均 (EMA), radam と正規化を行いましたが、自分たちのモデルには効果がありませんでした。特に、Wandbでパラメータチューニングを行った限りだと、正規化はしないほうが明らかにパフォーマンスが向上します。マルチタスク学習についてはCVへの寄与度はあまりありませんでしたが、アンサンブルにあたっては多様な特徴量が取れてよいのではないかと思います。</p>",
      "rawMarkdown": "（下に日本語による説明を書いています）\n\nWe would like to express our deepest gratitude to the competition hosts! We really enjoyed the competition for the entire period. \nThis write-up is separated in two parts: the observation, preprocessing and postprocessing by @chimaki821 then modeling, deep leaning by @tokkiwa. \n\n# 1. @chimaki821 Part (Observation + Preprocessing)\n\nI created this discussion using [PLaMo Translations](https://translate-demo.plamo.preferredai.jp/).\n\n## 1.1. Overview\n\nLike most participants, I implemented the following techniques:\n\n* Corrected left-handed data by inverting `acc_y`, `rot_x`, and `rot_y` values, swapping THM channels 3↔5, swapping ToF channels 3↔5, and inverting an 8×8 square region\n* Implemented multitask learning by adding `orientation` and `behavior` as auxiliary loss terms\n* Switched between IMU-only and full sensor model configurations\n* Applied corrections to subjects suspected of having mounting anomalies (partially successful, details follow)\n* Used weighted ensemble methods (though not included in the final submission, these proved effective in private testing)\n\nAfter implementing these baseline approaches, my teammate @tokkiwa significantly improved the model through various enhancements to the pipeline and ensemble methods with multiple models. This proved extremely valuable since I had virtually no background in machine learning.\n\nBelow, I describe several key techniques that helped inform the above approach, as well as additional methods I implemented:\n\n* Data observation and mounting correction\n* Data augmentation (DA) and test-time augmentation (TTA) using rotation around the y-axis\n* Creation of soft labels\n\nI also summarize:\n\n* What I tried during the competition that didn't work or was left unimplemented\n* Additional approaches I wanted to explore but couldn't implement in time\n\n\n## 1.2. Data Observation and Mounting Correction\n\nThis section was developed in collaboration with @epopaca.\n\nWe created a [notebook](https://www.kaggle.com/code/chimaki821/visualization-for-19th-place-solution) that, when provided with a specific subject and gesture, consolidates all relevant data into a single image. 81 subjects × 18 gestures = 1,458 images saved on my smartphone, constantly observing them whenever I had time.\n\ne.g. (SUBJ_000206, gesture: Above Ear - pull hair)\n![](https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F13641953%2F7da4b813f511805d152e32eb71f95fd9%2Fsubj_000206_gesture0.png?generation=1757046751796413&alt=media)\n\nUsing this visualization, we conducted analyses on left-hand correction and mounting adjustments. By examining `acc_x, acc_y, acc_z` values, we determined that `SUBJ_019262` and `SUBJ_045235` likely had improper mounting. Considering potential issues with the Herios sensor's attachment, there are four main possible scenarios (including normal conditions):\n(For reference on the sensor's xyz axis orientation, see this [discussion thread](https://www.kaggle.com/competitions/cmi-detect-behavior-with-sensor-data/discussion/588501#3243561)).\n\n\n| Normal (Original) | 180° Rotation around Y-axis |\n| ---- | ---------------------|\n|![](https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F13641953%2F761af7428925fdc6144ee1c66ea71c6a%2Fnormal.jpg?generation=1756997880870680&alt=media) | ![](https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F13641953%2F783d2a11e0901a534668f910c8fe99df%2Fy_180.jpg?generation=1756997909957729&alt=media) |\n| 180° Rotation around Z-axis | 180° Y-axis + 180° Z-axis |\n|![](https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F13641953%2Fcb100f0dfd78c0d775c6d05ed0d784e8%2Fz_180.jpg?generation=1756997918874301&alt=media)|![](https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F13641953%2F26926ea1c2c6b6ca34ef95a60be7945a%2Fy_180_z_180.jpg?generation=1756997926139663&alt=media)|\n\nConsidering the possibility that test data might also contain similar installation errors, we implemented a binary classification system to detect installation anomalies and apply correction during inference. However, since the number of relevant cases was limited in public testing, the effect was difficult to discern. Therefore, for this submission, we prioritized false positive reduction by setting the threshold to 0.9, resulting in only limited effectiveness. When lowering the threshold to 0.5 in the Late Submission, our score improved to 0.862 😢\n\n\n## 1.3. DA+TTA with Y-Axis Rotation\n\nBuilding on the assumption of fixed sensor orientation, we addressed the scenario where \"even with proper installation, minor rotation occurs around the y-axis.\" Therefore, we implemented:\n\n* During training: DA with random rotation around the y-axis using a uniform distribution between -15° and +15°\n* During inference: TTA that attempts multiple small rotation angles around the y-axis and averages the results\n\nBy setting the inference angles to `[0, +5, -5, +15, -15]`, we observed an average improvement of approximately +0.002 in public LB across multiple experiments. Private LB also showed similar performance gains.\n\nFor this implementation, we carefully applied the principle that \"rotation operations should be applied from the left to acceleration data and from the right to the rotation data.\"\n\n## 1.4. Creating Soft Labels\n\nThis section was collaboratively developed with @mckmckmck.\n\nWhile previous optimizations focused on \"preventing difficult subjects from being overlooked,\" when we examined OOF scores by gesture type, we identified certain gestures that were particularly challenging to predict. Here is an example from the IMU-only model:\n\n```\n-----------------------------------------------------\nF1 score for class[0] = 0.666 tp=417, fp=197, fn=221\nF1 score for class[1] = 0.643 tp=405, fp=214, fn=235\nF1 score for class[2] = 0.824 tp=548, fp=142, fn=92\nF1 score for class[3] = 0.442 tp=286, fp=369, fn=352\nF1 score for class[4] = 0.609 tp=384, fp=238, fn=256\nF1 score for class[5] = 0.538 tp=340, fp=284, fn=300\nF1 score for class[6] = 0.693 tp=444, fp=198, fn=196\nF1 score for class[7] = 0.517 tp=328, fp=305, fn=309\nF1 score for class[8] = 0.985 tp=2998, fp=54, fn=40\n-----------------------------------------------------\nMacro F1 = 0.6573605192818119\nBFRB Macro F1 = 0.6164599766386722\nScore = (Binary F1 + Macro F1) / 2 = 0.824077831125434\n```\n\n※ Class 8 represents non-BFRB gestures. Classes 0-7 correspond to:\n\n```\n0: Above ear - pull hair\n1: Forehead - pull hairline\n2: Forehead - scratch\n3: Eyebrow - pull hair\n4: Eyelash - pull hair\n5: Neck - pinch skin\n6: Neck - scratch\n7: Cheek - pinch skin\n```\n\nFurthermore, we visualized the confusion matrix for the BFRB category (where element (i, j) represents the number of sequences with true label i and predicted label j):\n\n![](https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F13641953%2F12ee9d467f73ad3ebf4d305b43340ae1%2Fimu011_bfrb.png?generation=1757038524229493&alt=media)\n\nFrom these observations, we identified:\n\n* The distinction between \"Eyebrow - pull hair\" and \"Eyelash - pull hair\" proves particularly difficult\n* Other similar gesture categories exist\n\nWe then introduced soft labeling after one-hot encoding, assigning small weights to similar gesture categories, which yielded modest but noticeable improvements overall.\n\n## 1.5. Unsuccessful Approaches and Additional Considerations\n\n**Areas for Improvement**\n\n* **Stacking**\n\n  * The weighted average of CV maximization weights calculated using Optuna was discarded as it resulted in lower public leaderboard scores (private score: 0.860–0.861 😢)\n  * Attempted stacking with LightGBM but found no improvement in CV performance, so abandoned this approach\n* **Alignment Correction**\n\n  * Due to uncertainty about the model's robustness and to avoid false positives, I increased the threshold, which only produced limited effectiveness (see Section 1.2)\n\n**Additional Approaches I Considered**\n\n* **Self-Supervised Learning and Adversarial Training**\n\n  * Although I wanted to experiment with these methods given the large number of subjects (81), lack of expertise prevented implementation\n* **Pre-trained Model Utilization**\n\n  * Referenced [this paper](https://www.nature.com/articles/s41746-024-01062-3) on self-supervised learning for human activity recognition and considered transfer learning, but ran out of time\n* **Rotation Correction Around the Y-Axis**\n\n  * Some data exhibited rotation around the y-axis (likely requiring -90° correction for SUBJ_011323 and +60° for SUBJ_032165)\n  * Since omitting this correction didn't cause significant accuracy degradation compared to other subjects, I prioritized other tasks\n  * If I had more time, I would have developed a model to predict y-axis rotation angles and implemented the correction\n\nIf anyone has successful implementations or insights regarding these areas, I'd be very grateful for your feedback!!\n\n\n\n# 2. @tokkiwa Part (Deep Learning & Modeling) \n\n## TL;DR\n- Channel-wise Convolution really matters (33rd place with single model!)\n- 3D CNN for TOF\n- Multitask Training \n\n## 2.1 Model\nOur main model consists of three parts; (1) Channel-wise Feature Extractor, (2) BiGRU and (3) MLP Head. \nThe latter two are not that interesting, so let's focus on the Feature Extractor. We first noticed that the channel-independent cnn models in [this notebook](https://www.kaggle.com/code/kaigaokaigao/lb-0-75-imu-only-multibranch-inference) performs better than other models. Here is the code snippet; \n```py\n        self.branches = nn.ModuleList(\n            [\n                nn.Sequential(\n                    MultiScaleConv1d(1, 12, kernel_sizes=[3, 5, 7]),\n                    ResidualSEBlock(36, 48, 3, dropout=0.3),\n                    ResidualSEBlock(48, 48, 3, dropout=0.3),\n                )\n                for _ in range(num_channels)\n            ]\n        )\n```\n\nThis model prepares cnn pipelines independently for each of the channels. As the sensor data have completely different modalities for each channels, it is reasonable to employ independent kernels to extract rich features. We further rewrite the branches with a grouped convolution, initially for the speed-up. Unexpectedly, combining the channel-independent convolution with inter-channel SE block performs much better than the fully channel-independent processing.\n\nAdditionally, our teammate @chimaki821 adopted a 3D-CNN for the TOF features to capture the spatio-temporal relationship of the sensors.\n\n```py\n           self.tof_branch = nn.Sequential(\n                nn.Conv3d(tof_sensors, 24 * tof_sensors, (5,2,2), padding='same', groups=tof_sensors, bias=False),\n                nn.BatchNorm3d(24 * tof_sensors), nn.ReLU(), nn.MaxPool3d((1,2,2)),\n                nn.Conv3d(24 * tof_sensors, tof_out_channels * tof_sensors, (7,2,2), padding='same', groups=tof_sensors, bias=False),\n                nn.BatchNorm3d(tof_out_channels * tof_sensors), nn.ReLU(),\n                nn.AdaptiveMaxPool3d((None, 1, 1)),\n            )\n```\n\nThe features are then fed to the BiGRU and Attention Pooling, added gaussian noise and finally input to the small MLP head. Please refer to our codes for more details. \nFor the submission, we trained some other variant (e.g. with FFT features added) of this model and other simple CNN model seen in the public notebooks. \n\n## 2.2 Traning\nWe used separated losses for behavior, orientation and gesture and summed them up (originally implemented by @chimaki821). \nSpecifically, cross entropy was applied for orientation and gesture (for gesture, label smoothing = 0.2 was our best practice) and a `nn.BCEWithLogitsLoss` to behavior. \nWe finally used Cosine Annealing LR, Adam and the following data augments: \n- random noise\n- random scaling\n- artificial drift\n- random overwrite\n- mixup\n\nFocal loss, exponential moving average, radam and normalization were implemented but not effective for our models. \nThe result of wandb sweep suggests that normalization of the columns affects badly on our models. The gain by multi-task training is also limited, but we suppose it might have a good effect on ensembling as the method enables the model to learn differently. \n\n---\n\n（日本語）\n\n# 1. @chimaki821 Part (Observation + Preprocess)\n\n## 1.1. 概要\n\n多くの参加者と同様に、以下を実施しました。\n\n* 左手データの補正（`acc_y`・`rot_x`・`rot_y` の反転、THM 3↔5 の swap、ToF 3↔5 の swap、8×8 正方形領域の反転）\n* マルチタスク学習（`orientation` と `behavior` を補助ロスに追加）\n* IMU のみ／全センサーでのモデル切替\n* 取り付け異常が疑われる被験者の補正（部分的に成功、後述）\n* 重み付きアンサンブル（最終提出には未採用だが private では有効）\n\nこれらの基本実装ののち、チームメイトの @tokkiwa がモデル改善・パイプライン整備・複数モデルのアンサンブルを進めてくれたことで、スコアが大幅に向上しました。私は機械学習について何も知らなかったため、大変助かりました。\n\n以下では、上記の方針検討に役立った取り組みと、それ以外に行った\n\n* データ観察と取り付け補正\n* y 軸回り回転による DA（Data Augmentation）＋ TTA（Test-Time Augmentation）\n* ソフトラベルの作成\n\nについて述べます。あわせて、\n\n* コンペ期間中に試したがうまくいかなかったこと・やりたかったこと\n\nもまとめます。\n\n## 1.2. データ観察と取り付け補正\n\nこのパートは @epopaca と共同で進めました。\n\nsubject と gesture を指定すると該当データを 1 枚の画像にまとめて出力する [notebook](https://www.kaggle.com/code/chimaki821/visualization-for-19th-place-solution) を作成しました。81人×18 gestures = 1,458 枚の画像をスマートフォンに保存し、時間があれば常にこれを観察していました。\n\n（出力例は英語版をご覧ください。）\n\nこの可視化を使いながら、左手補正や取り付け補正を検討しました。`acc_x, acc_y, acc_z` を見ると、`SUBJ_019262` と `SUBJ_045235` は取り付けが不適切だと推測できます。Herios sensor の取り付けミスを想定すると（正常を含め）大きく4通りが考えられます（センサーの xyz 軸の向きは [この discussion](https://www.kaggle.com/competitions/cmi-detect-behavior-with-sensor-data/discussion/588501#3243561) を参照）。\n\n（図は英語版をご覧ください。）\n\nテスト側にも同様の取り付け方ミスがある可能性を考え、2 値分類で取り付け異常を判定し、推論時に補正をかける仕組みを用意しました。ところが public では該当ケースが少なく効果が見えづらかったため、本提出では偽陽性抑制を優先して閾値を 0.9 に設定。その結果、効果は限定的でした。Late Submission で閾値を 0.5 に下げたところ、スコアが 0.862 まで上昇しました 😢\n\n## 1.3. y 軸回り回転による DA＋TTA\n\nセンサー軸が固定されている前提から、「正常取り付けのつもりでも y 軸回りにわずかな回転が入る」ケースを想定しました。そこで、\n\n* 学習時：y 軸回りに一様乱数（−15°〜＋15°）で回転させる DA\n* 推論時：y 軸回りの微小回転を複数角度で試し平均化する TTA\n\nを実装。推論角度を `[0, +5, -5, +15, -15]` としたところ、複数の実験で public LB が平均で約 +0.002 改善。private でも同程度の上昇を確認しました。\n\nこの実装を行うのにあたって、「加速度データには回転作用素を左から作用、回転データには回転作用素の逆作用素を右から作用」させることに注意しました。\n\n## 1.4. ソフトラベルの作成\n\nこのパートは @mckmckmck と共同で行いました。\n\nこれまでの工夫は「難しい被験者を取りこぼさない」ことを目指したものでしたが、OOF（out-of-fold）について gesture 別にスコアを求めてみると、予測が難しい gesture があることがわかりました。IMU Only モデルの例：\n\n```\n-----------------------------------------------------\nF1 of class[0] = 0.666 tp=417, fp=197, fn=221\nF1 of class[1] = 0.643 tp=405, fp=214, fn=235\nF1 of class[2] = 0.824 tp=548, fp=142, fn=92\nF1 of class[3] = 0.442 tp=286, fp=369, fn=352\nF1 of class[4] = 0.609 tp=384, fp=238, fn=256\nF1 of class[5] = 0.538 tp=340, fp=284, fn=300\nF1 of class[6] = 0.693 tp=444, fp=198, fn=196\nF1 of class[7] = 0.517 tp=328, fp=305, fn=309\nF1 of class[8] = 0.985 tp=2998, fp=54, fn=40\n-----------------------------------------------------\nMacro F1 = 0.6573605192818119\nBFRB Macro F1 = 0.6164599766386722\nScore = (BinaryF1 + Macro F1) / 2 = 0.824077831125434\n```\n\n※ class 8 は non-BFRB。class 0〜7 は以下：\n\n```\n0: Above ear - pull hair\n1: Forehead - pull hairline\n2: Forehead - scratch\n3: Eyebrow - pull hair\n4: Eyelash - pull hair\n5: Neck - pinch skin\n6: Neck - scratch\n7: Cheek - pinch skin\n```\n\nさらに、BFRB 部分の混同行列を可視化（(i, j) 成分は 正解が i 番目の gesture, 予測が j 番目の gesture となる sequence の数）\n\n（図は英語版をご覧ください）\n\nこれらの観察から、\n\n* “Eyebrow - pull hair” と “Eyelash - pull hair” は類似\n* その他にも類似 gesture が存在\n\nことが分かります。そこで one-hot の後に、類似 gesture に小さな重みを配分するソフトラベリングを導入したところ、全体的にわずかながら改善が得られました。\n\n## 1.5. うまくいかなかったこと／他にやりたかったこと\n\n**うまくいかなかったこと**\n\n* **Stacking**\n\n  * Optuna で CV 最大化の重みを求めた重み付き平均は、public LB が低下したため選択せず（private score: 0.860〜0.861 😢）\n  * LightGBM による stacking は CV 改善が見られず断念\n* **取り付け補正**\n\n  * モデルの強さに確信が持てず偽陽性を避けるため閾値を上げた結果、効果が限定的（§1.2 参照）\n\n**他にやりたかったこと**\n\n* **自己教師あり学習・対照学習**\n\n  * subject が 81 と多くないため試したかったが、知識不足で着手できず\n* **事前学習モデルの利用**\n\n  * [Self-supervised learning for human activity recognition using 700,000 person-days of wearable data](https://www.nature.com/articles/s41746-024-01062-3) を参照し、転移学習を検討したが時間不足\n* **y 軸回り回転の回帰補正**\n\n  * y 軸周りに回転してしまっているデータもある（おそらく SUBJ_011323 は -90度, SUBJ_032165 は +60度の補正が必要）\n  * やらなくても他の subject と比べて大幅な精度低下にはなっていなかったため優先しなかった\n  * 余裕があったら y 軸周りの回転角度を予測するモデルを作り、補正したかった\n\nこれらについて成功事例・検討事例があれば、ぜひ教えてください！！\n\n# 2. @tokkiwa part\n## TL;DR\n- グループ化畳み込みが有効 (1モデルで33位相当) \n- TOF に 3D CNN \n- マルチタスク学習\n\n## 2.1 Model\n\n自分たちのモデルは、(1)特徴量抽出、(2)BiGRU と (3) MLPの予測ヘッドからなります。後者二つはPublicにあるようなものなので、おもに、主に特徴抽出について説明します。\nはじめに、[この notebook](https://www.kaggle.com/code/kaigaokaigao/lb-0-75-imu-only-multibranch-inference) にあるチャンネル独立なモデルの性能が良いことに気づきました。\n\nこのモデルは次のように、各チャンネルについて完全に独立したCNNを用意して、各チャンネルを48倍に拡大しながら畳み込み処理をおこないます。\n```py\n        self.branches = nn.ModuleList(\n            [\n                nn.Sequential(\n                    MultiScaleConv1d(1, 12, kernel_sizes=[3, 5, 7]),\n                    ResidualSEBlock(36, 48, 3, dropout=0.3),\n                    ResidualSEBlock(48, 48, 3, dropout=0.3),\n                )\n                for _ in range(num_channels)\n            ]\n        )\n```\n今回のセンサーデータはそれぞれ全く違うモダリティがあるので、はじめに混ぜながら処理するよりも、独立に特徴量を得る方がリッチな特徴量を得られるためだと思います。\nもともとのモデルはfor文でチャンネルごとに処理をしていたので、グループ化畳み込みで高速化を行いました。この途中でわかったこととして、SEブロックのみは全チャンネルで作用させるとより性能が向上します。\nしたがって、最終的にIMUとTHM特徴量についてはチャンネル毎畳み込みで処理し、途中のSEブロックのみ全チャンネルで処理するというパイプラインになりました。\n\nTOF特徴量については空間的にセンサーが並んでいるので、3D-CNNを用いて空間方向と系列方向を別に扱うような形にしました。 ( @chimaki821 のアイデアです)\n\n```py\n           self.tof_branch = nn.Sequential(\n                nn.Conv3d(tof_sensors, 24 * tof_sensors, (5,2,2), padding='same', groups=tof_sensors, bias=False),\n                nn.BatchNorm3d(24 * tof_sensors), nn.ReLU(), nn.MaxPool3d((1,2,2)),\n                nn.Conv3d(24 * tof_sensors, tof_out_channels * tof_sensors, (7,2,2), padding='same', groups=tof_sensors, bias=False),\n                nn.BatchNorm3d(tof_out_channels * tof_sensors), nn.ReLU(),\n                nn.AdaptiveMaxPool3d((None, 1, 1)),\n            )\n```\n\n特徴量はその後にBiGRUとAttention Pooling に入力され、ガウスノイズを掛けてMLPヘッドに入力され、推論結果が出力されます。詳しくはコードを参照してください。\nアンサンブルについては、このモデルの変種 (FFT特徴量を入れたものなど)と、publicに見られたようなシンプルなCNNモデルを混ぜました。先述した回転のpostprocessing などを掛ける都合で、モデルは4~5個程度しか使えなかったので、あまり多くのモデルは使っていません。\n\n## 2.2 訓練\n\nbehavior, orientation, gesture にそれぞれ異なるロスを設定するマルチタスク学習を行いました。( @chimaki821  が実装してくれました) gesture,  orientation に対してはCross Entropy (gestureのみlabel_smoothing = 0.2), behavior については `nn.BCEWithLogitsLoss` を用いて、すべてのロスを足し合わせたものを用いました。\n\n最終的に、訓練には Cosine Annealing LR, Adam と、次のデータ拡張を用いました。\n\n- random noise\n- random scaling\n- drift (蓄積する時間ノイズの再現)\n- random overwrite\n- mixup\n\nFocal loss, 移動指数平均 (EMA), radam と正規化を行いましたが、自分たちのモデルには効果がありませんでした。特に、Wandbでパラメータチューニングを行った限りだと、正規化はしないほうが明らかにパフォーマンスが向上します。マルチタスク学習についてはCVへの寄与度はあまりありませんでしたが、アンサンブルにあたっては多様な特徴量が取れてよいのではないかと思います。\n\n\n",
      "votes": 16
    }
  ],
  "comments": [],
  "raw_markdown_by_id": {
    "3281462": "（下に日本語による説明を書いています）\n\nWe would like to express our deepest gratitude to the competition hosts! We really enjoyed the competition for the entire period. \nThis write-up is separated in two parts: the observation, preprocessing and postprocessing by @chimaki821 then modeling, deep leaning by @tokkiwa. \n\n# 1. @chimaki821 Part (Observation + Preprocessing)\n\nI created this discussion using [PLaMo Translations](https://translate-demo.plamo.preferredai.jp/).\n\n## 1.1. Overview\n\nLike most participants, I implemented the following techniques:\n\n* Corrected left-handed data by inverting `acc_y`, `rot_x`, and `rot_y` values, swapping THM channels 3↔5, swapping ToF channels 3↔5, and inverting an 8×8 square region\n* Implemented multitask learning by adding `orientation` and `behavior` as auxiliary loss terms\n* Switched between IMU-only and full sensor model configurations\n* Applied corrections to subjects suspected of having mounting anomalies (partially successful, details follow)\n* Used weighted ensemble methods (though not included in the final submission, these proved effective in private testing)\n\nAfter implementing these baseline approaches, my teammate @tokkiwa significantly improved the model through various enhancements to the pipeline and ensemble methods with multiple models. This proved extremely valuable since I had virtually no background in machine learning.\n\nBelow, I describe several key techniques that helped inform the above approach, as well as additional methods I implemented:\n\n* Data observation and mounting correction\n* Data augmentation (DA) and test-time augmentation (TTA) using rotation around the y-axis\n* Creation of soft labels\n\nI also summarize:\n\n* What I tried during the competition that didn't work or was left unimplemented\n* Additional approaches I wanted to explore but couldn't implement in time\n\n\n## 1.2. Data Observation and Mounting Correction\n\nThis section was developed in collaboration with @epopaca.\n\nWe created a [notebook](https://www.kaggle.com/code/chimaki821/visualization-for-19th-place-solution) that, when provided with a specific subject and gesture, consolidates all relevant data into a single image. 81 subjects × 18 gestures = 1,458 images saved on my smartphone, constantly observing them whenever I had time.\n\ne.g. (SUBJ_000206, gesture: Above Ear - pull hair)\n![](https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F13641953%2F7da4b813f511805d152e32eb71f95fd9%2Fsubj_000206_gesture0.png?generation=1757046751796413&alt=media)\n\nUsing this visualization, we conducted analyses on left-hand correction and mounting adjustments. By examining `acc_x, acc_y, acc_z` values, we determined that `SUBJ_019262` and `SUBJ_045235` likely had improper mounting. Considering potential issues with the Herios sensor's attachment, there are four main possible scenarios (including normal conditions):\n(For reference on the sensor's xyz axis orientation, see this [discussion thread](https://www.kaggle.com/competitions/cmi-detect-behavior-with-sensor-data/discussion/588501#3243561)).\n\n\n| Normal (Original) | 180° Rotation around Y-axis |\n| ---- | ---------------------|\n|![](https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F13641953%2F761af7428925fdc6144ee1c66ea71c6a%2Fnormal.jpg?generation=1756997880870680&alt=media) | ![](https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F13641953%2F783d2a11e0901a534668f910c8fe99df%2Fy_180.jpg?generation=1756997909957729&alt=media) |\n| 180° Rotation around Z-axis | 180° Y-axis + 180° Z-axis |\n|![](https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F13641953%2Fcb100f0dfd78c0d775c6d05ed0d784e8%2Fz_180.jpg?generation=1756997918874301&alt=media)|![](https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F13641953%2F26926ea1c2c6b6ca34ef95a60be7945a%2Fy_180_z_180.jpg?generation=1756997926139663&alt=media)|\n\nConsidering the possibility that test data might also contain similar installation errors, we implemented a binary classification system to detect installation anomalies and apply correction during inference. However, since the number of relevant cases was limited in public testing, the effect was difficult to discern. Therefore, for this submission, we prioritized false positive reduction by setting the threshold to 0.9, resulting in only limited effectiveness. When lowering the threshold to 0.5 in the Late Submission, our score improved to 0.862 😢\n\n\n## 1.3. DA+TTA with Y-Axis Rotation\n\nBuilding on the assumption of fixed sensor orientation, we addressed the scenario where \"even with proper installation, minor rotation occurs around the y-axis.\" Therefore, we implemented:\n\n* During training: DA with random rotation around the y-axis using a uniform distribution between -15° and +15°\n* During inference: TTA that attempts multiple small rotation angles around the y-axis and averages the results\n\nBy setting the inference angles to `[0, +5, -5, +15, -15]`, we observed an average improvement of approximately +0.002 in public LB across multiple experiments. Private LB also showed similar performance gains.\n\nFor this implementation, we carefully applied the principle that \"rotation operations should be applied from the left to acceleration data and from the right to the rotation data.\"\n\n## 1.4. Creating Soft Labels\n\nThis section was collaboratively developed with @mckmckmck.\n\nWhile previous optimizations focused on \"preventing difficult subjects from being overlooked,\" when we examined OOF scores by gesture type, we identified certain gestures that were particularly challenging to predict. Here is an example from the IMU-only model:\n\n```\n-----------------------------------------------------\nF1 score for class[0] = 0.666 tp=417, fp=197, fn=221\nF1 score for class[1] = 0.643 tp=405, fp=214, fn=235\nF1 score for class[2] = 0.824 tp=548, fp=142, fn=92\nF1 score for class[3] = 0.442 tp=286, fp=369, fn=352\nF1 score for class[4] = 0.609 tp=384, fp=238, fn=256\nF1 score for class[5] = 0.538 tp=340, fp=284, fn=300\nF1 score for class[6] = 0.693 tp=444, fp=198, fn=196\nF1 score for class[7] = 0.517 tp=328, fp=305, fn=309\nF1 score for class[8] = 0.985 tp=2998, fp=54, fn=40\n-----------------------------------------------------\nMacro F1 = 0.6573605192818119\nBFRB Macro F1 = 0.6164599766386722\nScore = (Binary F1 + Macro F1) / 2 = 0.824077831125434\n```\n\n※ Class 8 represents non-BFRB gestures. Classes 0-7 correspond to:\n\n```\n0: Above ear - pull hair\n1: Forehead - pull hairline\n2: Forehead - scratch\n3: Eyebrow - pull hair\n4: Eyelash - pull hair\n5: Neck - pinch skin\n6: Neck - scratch\n7: Cheek - pinch skin\n```\n\nFurthermore, we visualized the confusion matrix for the BFRB category (where element (i, j) represents the number of sequences with true label i and predicted label j):\n\n![](https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F13641953%2F12ee9d467f73ad3ebf4d305b43340ae1%2Fimu011_bfrb.png?generation=1757038524229493&alt=media)\n\nFrom these observations, we identified:\n\n* The distinction between \"Eyebrow - pull hair\" and \"Eyelash - pull hair\" proves particularly difficult\n* Other similar gesture categories exist\n\nWe then introduced soft labeling after one-hot encoding, assigning small weights to similar gesture categories, which yielded modest but noticeable improvements overall.\n\n## 1.5. Unsuccessful Approaches and Additional Considerations\n\n**Areas for Improvement**\n\n* **Stacking**\n\n  * The weighted average of CV maximization weights calculated using Optuna was discarded as it resulted in lower public leaderboard scores (private score: 0.860–0.861 😢)\n  * Attempted stacking with LightGBM but found no improvement in CV performance, so abandoned this approach\n* **Alignment Correction**\n\n  * Due to uncertainty about the model's robustness and to avoid false positives, I increased the threshold, which only produced limited effectiveness (see Section 1.2)\n\n**Additional Approaches I Considered**\n\n* **Self-Supervised Learning and Adversarial Training**\n\n  * Although I wanted to experiment with these methods given the large number of subjects (81), lack of expertise prevented implementation\n* **Pre-trained Model Utilization**\n\n  * Referenced [this paper](https://www.nature.com/articles/s41746-024-01062-3) on self-supervised learning for human activity recognition and considered transfer learning, but ran out of time\n* **Rotation Correction Around the Y-Axis**\n\n  * Some data exhibited rotation around the y-axis (likely requiring -90° correction for SUBJ_011323 and +60° for SUBJ_032165)\n  * Since omitting this correction didn't cause significant accuracy degradation compared to other subjects, I prioritized other tasks\n  * If I had more time, I would have developed a model to predict y-axis rotation angles and implemented the correction\n\nIf anyone has successful implementations or insights regarding these areas, I'd be very grateful for your feedback!!\n\n\n\n# 2. @tokkiwa Part (Deep Learning & Modeling) \n\n## TL;DR\n- Channel-wise Convolution really matters (33rd place with single model!)\n- 3D CNN for TOF\n- Multitask Training \n\n## 2.1 Model\nOur main model consists of three parts; (1) Channel-wise Feature Extractor, (2) BiGRU and (3) MLP Head. \nThe latter two are not that interesting, so let's focus on the Feature Extractor. We first noticed that the channel-independent cnn models in [this notebook](https://www.kaggle.com/code/kaigaokaigao/lb-0-75-imu-only-multibranch-inference) performs better than other models. Here is the code snippet; \n```py\n        self.branches = nn.ModuleList(\n            [\n                nn.Sequential(\n                    MultiScaleConv1d(1, 12, kernel_sizes=[3, 5, 7]),\n                    ResidualSEBlock(36, 48, 3, dropout=0.3),\n                    ResidualSEBlock(48, 48, 3, dropout=0.3),\n                )\n                for _ in range(num_channels)\n            ]\n        )\n```\n\nThis model prepares cnn pipelines independently for each of the channels. As the sensor data have completely different modalities for each channels, it is reasonable to employ independent kernels to extract rich features. We further rewrite the branches with a grouped convolution, initially for the speed-up. Unexpectedly, combining the channel-independent convolution with inter-channel SE block performs much better than the fully channel-independent processing.\n\nAdditionally, our teammate @chimaki821 adopted a 3D-CNN for the TOF features to capture the spatio-temporal relationship of the sensors.\n\n```py\n           self.tof_branch = nn.Sequential(\n                nn.Conv3d(tof_sensors, 24 * tof_sensors, (5,2,2), padding='same', groups=tof_sensors, bias=False),\n                nn.BatchNorm3d(24 * tof_sensors), nn.ReLU(), nn.MaxPool3d((1,2,2)),\n                nn.Conv3d(24 * tof_sensors, tof_out_channels * tof_sensors, (7,2,2), padding='same', groups=tof_sensors, bias=False),\n                nn.BatchNorm3d(tof_out_channels * tof_sensors), nn.ReLU(),\n                nn.AdaptiveMaxPool3d((None, 1, 1)),\n            )\n```\n\nThe features are then fed to the BiGRU and Attention Pooling, added gaussian noise and finally input to the small MLP head. Please refer to our codes for more details. \nFor the submission, we trained some other variant (e.g. with FFT features added) of this model and other simple CNN model seen in the public notebooks. \n\n## 2.2 Traning\nWe used separated losses for behavior, orientation and gesture and summed them up (originally implemented by @chimaki821). \nSpecifically, cross entropy was applied for orientation and gesture (for gesture, label smoothing = 0.2 was our best practice) and a `nn.BCEWithLogitsLoss` to behavior. \nWe finally used Cosine Annealing LR, Adam and the following data augments: \n- random noise\n- random scaling\n- artificial drift\n- random overwrite\n- mixup\n\nFocal loss, exponential moving average, radam and normalization were implemented but not effective for our models. \nThe result of wandb sweep suggests that normalization of the columns affects badly on our models. The gain by multi-task training is also limited, but we suppose it might have a good effect on ensembling as the method enables the model to learn differently. \n\n---\n\n（日本語）\n\n# 1. @chimaki821 Part (Observation + Preprocess)\n\n## 1.1. 概要\n\n多くの参加者と同様に、以下を実施しました。\n\n* 左手データの補正（`acc_y`・`rot_x`・`rot_y` の反転、THM 3↔5 の swap、ToF 3↔5 の swap、8×8 正方形領域の反転）\n* マルチタスク学習（`orientation` と `behavior` を補助ロスに追加）\n* IMU のみ／全センサーでのモデル切替\n* 取り付け異常が疑われる被験者の補正（部分的に成功、後述）\n* 重み付きアンサンブル（最終提出には未採用だが private では有効）\n\nこれらの基本実装ののち、チームメイトの @tokkiwa がモデル改善・パイプライン整備・複数モデルのアンサンブルを進めてくれたことで、スコアが大幅に向上しました。私は機械学習について何も知らなかったため、大変助かりました。\n\n以下では、上記の方針検討に役立った取り組みと、それ以外に行った\n\n* データ観察と取り付け補正\n* y 軸回り回転による DA（Data Augmentation）＋ TTA（Test-Time Augmentation）\n* ソフトラベルの作成\n\nについて述べます。あわせて、\n\n* コンペ期間中に試したがうまくいかなかったこと・やりたかったこと\n\nもまとめます。\n\n## 1.2. データ観察と取り付け補正\n\nこのパートは @epopaca と共同で進めました。\n\nsubject と gesture を指定すると該当データを 1 枚の画像にまとめて出力する [notebook](https://www.kaggle.com/code/chimaki821/visualization-for-19th-place-solution) を作成しました。81人×18 gestures = 1,458 枚の画像をスマートフォンに保存し、時間があれば常にこれを観察していました。\n\n（出力例は英語版をご覧ください。）\n\nこの可視化を使いながら、左手補正や取り付け補正を検討しました。`acc_x, acc_y, acc_z` を見ると、`SUBJ_019262` と `SUBJ_045235` は取り付けが不適切だと推測できます。Herios sensor の取り付けミスを想定すると（正常を含め）大きく4通りが考えられます（センサーの xyz 軸の向きは [この discussion](https://www.kaggle.com/competitions/cmi-detect-behavior-with-sensor-data/discussion/588501#3243561) を参照）。\n\n（図は英語版をご覧ください。）\n\nテスト側にも同様の取り付け方ミスがある可能性を考え、2 値分類で取り付け異常を判定し、推論時に補正をかける仕組みを用意しました。ところが public では該当ケースが少なく効果が見えづらかったため、本提出では偽陽性抑制を優先して閾値を 0.9 に設定。その結果、効果は限定的でした。Late Submission で閾値を 0.5 に下げたところ、スコアが 0.862 まで上昇しました 😢\n\n## 1.3. y 軸回り回転による DA＋TTA\n\nセンサー軸が固定されている前提から、「正常取り付けのつもりでも y 軸回りにわずかな回転が入る」ケースを想定しました。そこで、\n\n* 学習時：y 軸回りに一様乱数（−15°〜＋15°）で回転させる DA\n* 推論時：y 軸回りの微小回転を複数角度で試し平均化する TTA\n\nを実装。推論角度を `[0, +5, -5, +15, -15]` としたところ、複数の実験で public LB が平均で約 +0.002 改善。private でも同程度の上昇を確認しました。\n\nこの実装を行うのにあたって、「加速度データには回転作用素を左から作用、回転データには回転作用素の逆作用素を右から作用」させることに注意しました。\n\n## 1.4. ソフトラベルの作成\n\nこのパートは @mckmckmck と共同で行いました。\n\nこれまでの工夫は「難しい被験者を取りこぼさない」ことを目指したものでしたが、OOF（out-of-fold）について gesture 別にスコアを求めてみると、予測が難しい gesture があることがわかりました。IMU Only モデルの例：\n\n```\n-----------------------------------------------------\nF1 of class[0] = 0.666 tp=417, fp=197, fn=221\nF1 of class[1] = 0.643 tp=405, fp=214, fn=235\nF1 of class[2] = 0.824 tp=548, fp=142, fn=92\nF1 of class[3] = 0.442 tp=286, fp=369, fn=352\nF1 of class[4] = 0.609 tp=384, fp=238, fn=256\nF1 of class[5] = 0.538 tp=340, fp=284, fn=300\nF1 of class[6] = 0.693 tp=444, fp=198, fn=196\nF1 of class[7] = 0.517 tp=328, fp=305, fn=309\nF1 of class[8] = 0.985 tp=2998, fp=54, fn=40\n-----------------------------------------------------\nMacro F1 = 0.6573605192818119\nBFRB Macro F1 = 0.6164599766386722\nScore = (BinaryF1 + Macro F1) / 2 = 0.824077831125434\n```\n\n※ class 8 は non-BFRB。class 0〜7 は以下：\n\n```\n0: Above ear - pull hair\n1: Forehead - pull hairline\n2: Forehead - scratch\n3: Eyebrow - pull hair\n4: Eyelash - pull hair\n5: Neck - pinch skin\n6: Neck - scratch\n7: Cheek - pinch skin\n```\n\nさらに、BFRB 部分の混同行列を可視化（(i, j) 成分は 正解が i 番目の gesture, 予測が j 番目の gesture となる sequence の数）\n\n（図は英語版をご覧ください）\n\nこれらの観察から、\n\n* “Eyebrow - pull hair” と “Eyelash - pull hair” は類似\n* その他にも類似 gesture が存在\n\nことが分かります。そこで one-hot の後に、類似 gesture に小さな重みを配分するソフトラベリングを導入したところ、全体的にわずかながら改善が得られました。\n\n## 1.5. うまくいかなかったこと／他にやりたかったこと\n\n**うまくいかなかったこと**\n\n* **Stacking**\n\n  * Optuna で CV 最大化の重みを求めた重み付き平均は、public LB が低下したため選択せず（private score: 0.860〜0.861 😢）\n  * LightGBM による stacking は CV 改善が見られず断念\n* **取り付け補正**\n\n  * モデルの強さに確信が持てず偽陽性を避けるため閾値を上げた結果、効果が限定的（§1.2 参照）\n\n**他にやりたかったこと**\n\n* **自己教師あり学習・対照学習**\n\n  * subject が 81 と多くないため試したかったが、知識不足で着手できず\n* **事前学習モデルの利用**\n\n  * [Self-supervised learning for human activity recognition using 700,000 person-days of wearable data](https://www.nature.com/articles/s41746-024-01062-3) を参照し、転移学習を検討したが時間不足\n* **y 軸回り回転の回帰補正**\n\n  * y 軸周りに回転してしまっているデータもある（おそらく SUBJ_011323 は -90度, SUBJ_032165 は +60度の補正が必要）\n  * やらなくても他の subject と比べて大幅な精度低下にはなっていなかったため優先しなかった\n  * 余裕があったら y 軸周りの回転角度を予測するモデルを作り、補正したかった\n\nこれらについて成功事例・検討事例があれば、ぜひ教えてください！！\n\n# 2. @tokkiwa part\n## TL;DR\n- グループ化畳み込みが有効 (1モデルで33位相当) \n- TOF に 3D CNN \n- マルチタスク学習\n\n## 2.1 Model\n\n自分たちのモデルは、(1)特徴量抽出、(2)BiGRU と (3) MLPの予測ヘッドからなります。後者二つはPublicにあるようなものなので、おもに、主に特徴抽出について説明します。\nはじめに、[この notebook](https://www.kaggle.com/code/kaigaokaigao/lb-0-75-imu-only-multibranch-inference) にあるチャンネル独立なモデルの性能が良いことに気づきました。\n\nこのモデルは次のように、各チャンネルについて完全に独立したCNNを用意して、各チャンネルを48倍に拡大しながら畳み込み処理をおこないます。\n```py\n        self.branches = nn.ModuleList(\n            [\n                nn.Sequential(\n                    MultiScaleConv1d(1, 12, kernel_sizes=[3, 5, 7]),\n                    ResidualSEBlock(36, 48, 3, dropout=0.3),\n                    ResidualSEBlock(48, 48, 3, dropout=0.3),\n                )\n                for _ in range(num_channels)\n            ]\n        )\n```\n今回のセンサーデータはそれぞれ全く違うモダリティがあるので、はじめに混ぜながら処理するよりも、独立に特徴量を得る方がリッチな特徴量を得られるためだと思います。\nもともとのモデルはfor文でチャンネルごとに処理をしていたので、グループ化畳み込みで高速化を行いました。この途中でわかったこととして、SEブロックのみは全チャンネルで作用させるとより性能が向上します。\nしたがって、最終的にIMUとTHM特徴量についてはチャンネル毎畳み込みで処理し、途中のSEブロックのみ全チャンネルで処理するというパイプラインになりました。\n\nTOF特徴量については空間的にセンサーが並んでいるので、3D-CNNを用いて空間方向と系列方向を別に扱うような形にしました。 ( @chimaki821 のアイデアです)\n\n```py\n           self.tof_branch = nn.Sequential(\n                nn.Conv3d(tof_sensors, 24 * tof_sensors, (5,2,2), padding='same', groups=tof_sensors, bias=False),\n                nn.BatchNorm3d(24 * tof_sensors), nn.ReLU(), nn.MaxPool3d((1,2,2)),\n                nn.Conv3d(24 * tof_sensors, tof_out_channels * tof_sensors, (7,2,2), padding='same', groups=tof_sensors, bias=False),\n                nn.BatchNorm3d(tof_out_channels * tof_sensors), nn.ReLU(),\n                nn.AdaptiveMaxPool3d((None, 1, 1)),\n            )\n```\n\n特徴量はその後にBiGRUとAttention Pooling に入力され、ガウスノイズを掛けてMLPヘッドに入力され、推論結果が出力されます。詳しくはコードを参照してください。\nアンサンブルについては、このモデルの変種 (FFT特徴量を入れたものなど)と、publicに見られたようなシンプルなCNNモデルを混ぜました。先述した回転のpostprocessing などを掛ける都合で、モデルは4~5個程度しか使えなかったので、あまり多くのモデルは使っていません。\n\n## 2.2 訓練\n\nbehavior, orientation, gesture にそれぞれ異なるロスを設定するマルチタスク学習を行いました。( @chimaki821  が実装してくれました) gesture,  orientation に対してはCross Entropy (gestureのみlabel_smoothing = 0.2), behavior については `nn.BCEWithLogitsLoss` を用いて、すべてのロスを足し合わせたものを用いました。\n\n最終的に、訓練には Cosine Annealing LR, Adam と、次のデータ拡張を用いました。\n\n- random noise\n- random scaling\n- drift (蓄積する時間ノイズの再現)\n- random overwrite\n- mixup\n\nFocal loss, 移動指数平均 (EMA), radam と正規化を行いましたが、自分たちのモデルには効果がありませんでした。特に、Wandbでパラメータチューニングを行った限りだと、正規化はしないほうが明らかにパフォーマンスが向上します。マルチタスク学習についてはCVへの寄与度はあまりありませんでしたが、アンサンブルにあたっては多様な特徴量が取れてよいのではないかと思います。\n\n\n"
  }
}