第2回日本人工知能オリンピックで総合3位(選抜枠3位)をいただいた解法を振り返ります。
Private LB: 0.6970(ベースライン: 1.1283)
はしがき
執筆に先立ちまして、昨年に引き続き、最高なコンペティションの機会を与えてくださったJOAI委員会のみなさまに感謝申し上げます。
- 本記事は、JOAI 2026 の問題設定をすでに把握している読者を主な対象としています。
- Element138は本記事を CC BY 4.0 ライセンスのもと公開します。
概要
- 時間方向の操作中心の特徴量作成(1・2階差分など)やメタデータの埋め込みの上で、TransformerとBiLSTMをバックボーンとして用いることで、良好な成果が得られました。
- また、マルチタスク学習、激しいデータオーグメンテーションに加え、擬似ラベリングや事前学習といった、テストデータも活用するアプローチを用いたことが、主に後半戦でスコアを後押ししました。
解法
概観
- Transformer Encoder・BiLSTMの2モデルそれぞれで、レバー位置とその2階差分までを予測するマルチタスク学習を行っています。
- わずか2つのモデルの予測を単に平均しているだけである点で、かなりシンプルな部類であると思います。
特徴量・前処理
最終的に利用した点
- 脳活動には、時系列データの定石である1・2階差分を適用しました。
- ノイジーなデータの平滑化に有効なローリング平均・標準偏差(ウィンドウ=5)およびEMA(α=0.3)も用いました。
もっとも、ノイズはかなり控えめであったので、モデルがある程度成熟してからは削除してもよかったかもしれません。
- マウス自体の埋め込みと、(マウス, 実験日) ペアの埋め込みを行いました。
レバー値を統計する簡易なEDAの結果、マウスごと・(マウス, 実験日) ペアごとの値の傾向があることは判明しましたが、マウス間に共通の、個々の実験日の傾向は視認できませんでした。したがって、実験日自体の埋め込みから方針を変更しました。
マウスの強化・行動パターンには個体差が大きいので、全てのマウスに共通の、日ごとの傾向を見つけるのは難しいものと解することができます。 - シーケンス内の正規化時刻(シーケンス先頭0~末尾1)も特徴に含めました。
しかし、今回はシーケンスの特定部分にレバーの変化が集中するような傾向はみられませんでしたので、あまり意味のある特徴量ではなかったかと思います。
- 脳活動データの標準化は、テストサンプルも含めた37万行のデータ全体を用いて行いました。
これにより、競技後期に0.0007(Private LB)のスコア改善をみました。
効果が出ずやめた点
- 脳活動データについて、3階以上の差分、累積値、ラグ・逆ラグ、スペクトル特性、自己相関係数、左右の和差などを1つずつ追加し実験しましたが、性能は改善しませんでした。
脳の22関心領域ごとの左右差が効果を見せなかったのは意外でした。レバーを片方の前肢のみで動かす場合が多いものと考えたためです。ところが思い返してみると、左右差データからの差分やEMAなどの加工特徴量を作成しそびれていたような気もします。これは痛手であったでしょう。
- 各日の実験開始からの絶対時間(0~約1800秒)を特徴に加えましたが、性能は改善しませんでした。
- 前のタイムステップからの経過時間も特徴に含めましたが、ほぼ全く変動しない値だったこともあり、性能は改善しませんでした。
- (マウス, 実験日) ペアごとの脳活動データ標準化も試しましたが、性能は改善しませんでした。
試し(きれ)ていない点
- 脳活動チャンネル同士の相関係数は試すことができていません。
- 脳活動データについて、Zhang, et al. の研究に倣ってMVG(Multiplex Visibility Graph)から構成した隣接行列 (B, T, T) を作成することで、2D CNN が適用できるようになるという案がありましたが、試すことができていません。
当初、レバーが0付近で一切動かないシーケンスをまず判別し、その次に具体的な値を回帰する、という2ステージ学習を試みていました。その際ステージ1の分類器としてこのMVGのアプローチを利用してみたところ、AUC 0.88という高精度を得ましたが、その予測結果をステージ2に特徴として渡すというナイーブなアプローチが禍したのか、結局性能の改善につながらなかったため、2ステージ学習のコンセプトと共に葬ってしまいました。もったいないことをしました。
- PCAで264次元を96次元に落とすことを試しましたが、性能の改善がみられなかったためそれ以上実験しませんでした。
しかしながら、実験は不十分でした。訓練データとテストデータを合わせてPCAを行えば性能が改善した可能性があります(トランズダクティブ学習に慣れておらず、忘れてしまっていました)。また、次元の圧縮率が高すぎた可能性もあります。
モデルアーキテクチャ
最終的に利用した点
- 1つ目のモデルには、1D CNN → Transformer Encoder を用いました。
- 1D CNN については、様々なスケールの特徴を抽出できるよう、3・5・7の異なる3つのカーネルサイズを用い、これらの出力を結合しています。残差接続があります。
ここで(マルチチャンネル)1D CNN は、チャンネルを超えた時間方向の局所特徴抽出に役立っています。
- 次にメタデータを挿入して393次元となります。ここでFC層を挟み、192次元に減らします。
- 次に学習可能な位置(時間)エンコーディングを行います。
- 次に Transformer Encoder を用います。4ヘッド、4層です。FFNは512次元です。残差接続があります。
- 最後に192次元を512次元にし、続いて128次元にするMLPを挟みます。タスクヘッドは3つ(レバー位置・1階差分・2階差分)です。
- 1D CNN については、様々なスケールの特徴を抽出できるよう、3・5・7の異なる3つのカーネルサイズを用い、これらの出力を結合しています。残差接続があります。
- 2つ目のモデルには、1D CNN → BiLSTM を用いました。
- Transformerブランチと異なり、真っ先にメタデータを挿入し、393次元にします。
特段のラショナールはないのですが、実験の結果この順番の方が性能が高くなりました。ブランチ間の多様性が増すこと自体が性能に寄与しているのでしょうか。
- 次に 1D CNN を用います。カーネルサイズ7→5→3→1を順に適用(スタック)しています。256次元に落としています。残差接続があります。
直感的に、スタックするよりTransformerブランチのように並列させた方がよいように感じるのですが、実験の結果スタックさせた方が性能が高くなりました。やはりブランチ間の多様性が増すためなのでしょうか。
- 次に3層のBiLSTMを用います。
- 最後にやはり256次元を512次元にし、続いて128次元にするMLPを挟みます。タスクヘッドはこちらも3つです。
- Transformerブランチと異なり、真っ先にメタデータを挿入し、393次元にします。
- 仔細の説明は割愛いたしますが、ドロップアウト(基本すべて0.15)やBatchNorm・LayerNormなどの適切な処理を随所で行っています。
効果が出ずやめた点
- BiTCN(Non-causal TCN)を3本目のブランチとして加えましたが、性能は改善しませんでした。
しかしTCNがNon-causalな多変数時系列解析に有用であることは知られていますから、この原因としては、膨張(Dilation)のハイパーパラメータにミスを犯したか、データが小さすぎたか短すぎた(今までにTCNを用いた経験がないので不明ですが…)というところになるかと思います。
- Axial Attention によって、脳活動チャンネル方向と時間方向のどちらもSelf-Attentionすることを考えましたが、性能は向上しませんでした。
試し(きれ)ていない点
- GNNアプローチは試すことができていません。
非常に面白そうではあったものの、このアプローチを知った時点では既に先述の通りMVGの利用が失敗していたため、同じくエキゾチックにみえたGNNの利用は一旦見送り、そのまま競技終了まで試すに至れませんでした。
また、チャンネル同士からどのようなグラフを構成すべきなのか悩ましいところです。関心領域の機能の類似性や、グローバルな相関の大きさに基づいて接続すればよいのでしょうか。差分などの派生特徴量はどうすればよいのでしょう。 - iTransformerおよびPatchTSTは競技初期に少し試しましたが、性能の改善がみられなかったためそれ以上実験しませんでした。
しかし、後述の通り、当時は実験速度をひどく重視したせいでホールドアウト1つのみで実験評価を行っていたため(何をしていたのか!)、この結果には相当の疑いがあるのです。どちらも、少なくともアンサンブルの材料としては大いに効果が見込めるように思えます。
- 全く毛色の異なるアーキテクチャ(LightGBMなど)は試すことができていません。
すっかり眼中から外れてしまっていました。アンサンブルのため色々と試してみるべきでした。
タスクおよび損失
最終的に利用した点
- 3つのヘッドで、レバーの位置およびその1・2階差分を予測する3タスク学習を行うものとしました。これら3つそれぞれのMSEを単純に加算したものをもって損失としました。
損失の比重を変えたり、3階以上の差分を加えたりしても性能は改善しませんでした。差分予測の損失にMSE以外(MAE、Huber、平均平方根誤差など)を用いても同様でした。
- 2つのモデルで学習は全く別々に行うものとしました(別々のヘッド、別々の損失、別々の勾配)。
アンサンブル効果を得ています。
効果が出ずやめた点
- マルチタスク学習ではなく、レバー位置を予測するヘッド1つのみに対し1・2階差分のMSEを補助損失とするアプローチも試しましたが、性能は改善しませんでした。
- レバー値の回帰を分類に置き換えるアプローチ、および新たなタスクとして分類を加えた4タスク学習を試しましたが、性能は改善しませんでした。
レバー値をうまく丸めると、わずか34種類の値ですべての値をMSE 0.001未満の精度で表せることが判明したので、34クラス分類として解くことができると考えました(差分ヘッドは引き続き回帰で解く)。
値の近さを加味した「ソフトな」損失も試しましたが、結局普通のクロスエントロピー損失が最もマシな精度となりました。ラベル平滑化は効果がありましたが、それでも純粋な回帰問題として解いた場合の性能には及びませんでした。 - そのタイムステップでレバーの動きがわずかでもあったか否か2値分類するタスクの追加も試しましたが、性能は改善しませんでした。
これは、脳活動のパターンは、マウスの力の変化の度合い(レバーの動きの度合い)に対しては連続的に変化するもので、すなわちレバーを動かしていないときとレバーをごくわずかに動かしているときの間には著しい脳活動の違いはないものと考えられます。
- レバー位置にHuber損失を用いても、性能は改善しませんでした。
試していない点
- 真のレバー値の大小などに応じた損失のスケーリングは試すことができていません。
例えば、レバー値が0付近である場合に損失のスケールを大きくすれば、0付近のレバー値の予測精度が上昇することを見込めるといった動機が考えられます。しかしながら、参考にした類似コンペと異なり本問では、EDAから訓練データとテストデータの間に分布の違いが乏しそうであることがわかりますから、テストデータでレバーの値が0付近であるサンプルが多くなるようなことは考えにくいため、特に試す必要はなかったかと考えます。
- レバー値の対数を予測対象とするアプローチは試すことができていません。
データオーグメンテーション
最終的に利用した点
- 50%の確率で、標準偏差0.02のガウシアンノイズをシーケンスの脳活動データに適用しました。
標準化後に適用していますから、0.02というのは、各チャンネルの標準偏差の0.02倍という意味になります。
- 50%の確率で、Mixupをシーケンスの脳活動データ・正規化時刻・レバー値(1・2階差分含む)に適用しました(ブレンドする割合は、α=β=0.4の第1種ベータ分布に従う)。
2つのシーケンスの中間値を意味化するのは難しいと考えていたため、当初これがうまくいったことには驚きました。しかし、264+1次元の空間内で中間点も連続的にグラウンディングされることに意味があるということなのでしょう。
ところで、マウス・(マウス, 実験日) ペアの埋め込みに関しては、Mixupするものではないと思ってMixupの対象から外してしまっていたのですが、後から調べたところ、どうもそれはまずかったようです。 - 20%の確率で、CutMixをシーケンスの脳活動データ・レバー値(1・2階差分含む)に適用しました(ランダムに連続する20~50%のタイムステップを置換します)。
こちらでもやはり、埋め込みも線形補間した方がよかったのでしょうか。
- 推論時には、生のシーケンスに加え、標準偏差0.01のガウシアンノイズを加えた4つの同シーケンスに対しても予測するTTAを行いました。
擬似ラベリング時にも同様のことを標準偏差0.015で行いました。こちらはPLTA(Pseudo-Labeling-Time Augmentation)とでも呼ぶのでしょうか。
効果が出ずやめた点
- シーケンスを時間方向に細切れにしたときの各ウィンドウ内で、タイムステップをランダムに並び替える、いわゆるシャフリングを試しましたが、性能は改善しませんでした。
- チャンネルマスキングも試してみましたが、性能は改善しませんでした。
脳活動データ、とりわけ差分チャンネルなどで0という値が頻出のことから、0でマスキングしてはまずいのは想像に難くありません。なお、時間方向のマスキングは学習可能なマスクにすることでうまくいきました(しかし、ずさんなバージョン管理のせいで最終的な実装から漏らすという痛恨のミスを犯しました)。
試していない点
- 左右の入れ替えは試すことができていません。
EDAの結果、左右の脳活動データは多分に非対称性を含んでいたので、左右を入れ替えてはまずいかと思います。
- シーケンス全体でチャンネルを入れ替える、いわゆるチャンネルシャフリングは試すことができていません。
詳しく検証してはいませんが、今回の問題では、すべてのチャンネルが異なる関心領域のデータですから、入れ替えては大変まずいように思えます。
半教師あり学習・自己教師あり学習
最終的に利用した点
- 擬似ラベリングを行いました。予測の信頼性が全テストデータの中で70パーセンタイル以上のシーケンスの予測結果を、新たに訓練データに加えました。
「信頼性」については、先述の(仮称)PLTAを用いた並行予測を実行し、それらの予測間の標準偏差が小さいほど、アンサンブル結果(予測間の平均)の信頼性が高いものとしました。
- 競技最終盤に、テストデータも活用した事前学習フェーズを設けました。全サンプルの脳活動データのみを用いた Pretext Task 3つを用いて、ヘッド以外のすべての重みを事前学習しました(ただし凍結はしません)。
バックボーン全体をいっぺんに更新するわけですから、勾配は1つです。- MAE(Masked Autoencoder)では、40%のタイムステップがマスクされたシーケンスから元データの復元を試みます。損失の重みは1としました。
- Denoisingでは、標準偏差0.15のガウシアンノイズを加え、さらに各チャンネルを10%の確率で0マスキングしたものから、復元を試みます。損失の重みは0.05としました。
0マスキングはまずかったかもしれません。
- VICRegでは、各シーケンスからオーグメンテーション(Denoisingと同様のランダム加工を適用したもの)2つを生成し、同じシーケンスのオーグメンテーションのみを互いに近い位置に埋め込むことを試みます。損失はV:I:C=25:25:1としました。損失の重みは0.16としました。
- タスクごとの損失のスケールも加味すると、影響の大きい順に、VICReg、MAE、Denoisingとなりました。
効果が出ずやめた点
- 擬似ラベリングの複数ステージ化を試みましたが、性能は改善しませんでした。
第2ステージで Validation Loss が改善した例がほとんどありませんでした。予測の誤差が蓄積してしまっている可能性があるため、擬似ラベリング結果の信頼性しきい値を、より保守的にすべきであったかもしれません。
試していない点
- ラベル伝播法などは試すことができていません。
そもそも回帰タスクにどうやってラベル伝播法を適用するのでしょうか。平均値を伝播していくのでしょうか。
CVおよびアンサンブル
最終的に利用した点
- なぜかモンテカルロCV(MCCV)を用いました(マネしないでください!)。
ここは大きな反省点です。競技序盤、実験速度を重視するあまりホールドアウト法を実装してしまいました。中盤に差し掛かったときにさすがにまずいと考えてCVへの移行を決めましたが、普通のCVに変更すると当時実験中であったモデルとスプリットが変わってしまい、単純なスコア比較ができなくなるので、ホールドアウトをシードを変えながら繰り返すMCCVを妥協点と思ってしまったのです。これは気の迷いでした。
そもそもそれまで測ってきたスコア自体の信頼性が乏しいのですから、それと新スコアを比較すること自体どだい無理な話だったのです。したがって、より信頼性のある普通のCVに移行すべきでした。
とはいえ、シーケンスごとのグルーピングが破られたわけではないので、リークなどの重大な問題にはつながりませんでした。 - Validation Loss の悪かった20%の試行(シード)はアンサンブル材料から外しました(マネしないでください!)。
後から行った実験により、これはまずかったことが判明しています。単にアンサンブルの多様性を減らすだけの悪手であったようです。
- 事前学習なし・事前学習付きモデルの両方でシードバギングしたものの平均をとりました。
効果が出ずやめた点
- 平均の代わりに中央値を用いたアンサンブル(シードバギング)を試みましたが、性能は改善しませんでした。
回帰(少なくともMSE)タスクにおいてはたいてい平均をとった方がよい、という体感に合致します。
- フォールド(否、ホールドアウト…)ごとに (マウス, 実験日) ペアで層化するスプリットも試したのですが、性能は改善しませんでした。
(マウス, 実験日) ペアは全部で350種類ほどもあるので、たった6000本(7000本×0.85)のシーケンスしか訓練に用いないのなら、訓練対象の中にほぼ全く現れない (マウス, 実験日) ペアがあるのが常だろうと危惧し、わりあい時間をかけて試行錯誤してしまったのですが、そもそもその感覚は数学的に誤りであることが判明しました。
時間をかけて何かを実装してもうまくいかないときは、その実装の目的となっている「仮定・仮説」を批判的に検証すべきなのでした。
試していない点
- まともなスタッキングは試すことができていません。
たった2モデルのアンサンブルでは、スタッキングするほどの多様性もないように思えます。
もっとも、それ自体が問題です。
その他
効果が出ずやめた点
- レバーが0付近で一切動かないシーケンスである確率をまず予測し、その出力を1つの特徴(シーケンスレベルのメタデータ)として具体的な値を回帰する、という2ステージ学習を試みましたが、性能は改善しませんでした。
レバーが常に0付近で一切動かないシーケンスは、訓練データ全体の約40%に上ります。何の行動も起こしていない期間の脳活動には一貫した安定性(などの特殊性)があっておかしくないですから、これを判別すべきではないかと考えました。
実を言うと、こういう類の2ステージ学習を試みた経験がなく、このアプローチ自体が正しいのか大いに疑問があります。ステージ1の判別性能はAUC 0.88とまずまず良好ですが、出力をそのまま特徴として用いてよいほどの性能ではないということなのでしょうか。
なお、もしステージ1で完璧にシーケンス分類を行った場合、最終的な Validation Loss は0.25ほど下がりうることがわかっています(当時のMCCVでは 0.81 → 0.56)。
ひょっとすると、このような2ステージ制ではなく、タスクの1つとして用いるべきなのかもしれません。もっとも、先述の通り、タイムステップごとにレバー値の変化があったか否か2値分類するタスクは、特に好影響をもたらさないことがわかっていますが……
試していない点
- カルマン平滑化などを用いた後処理は試すことができていません。
予測データを見たところ、そこまで予測値の上下は激しくありませんが、それでも試すべきではあったかもしれません。
まとめ
やってよかった点
具体的な解法について
- 時系列データに適切な特徴量(1・2階差分など)を作成し、メタデータの埋め込みも怠らなかったこと
- 1D CNN からのTransformerとBiLSTMという、多変量性・双方向性の両方を利用した、堅実なバックボーンを構築したこと
ここまでで、0.74(Private LB)が得られました。性能の向上に最も寄与した点でした。
- レバー位置の1・2階差分のマルチタスク学習を利用したこと
位置の時間差分は、単体で学習する意味がある量と考えられます。
- 効果が薄そうだと思っても、データオーグメンテーション(Mixup・CutMix)を試してみたこと
- テストデータも活用する術を考えたこと(擬似ラベリング、グローバル標準化、事前学習)
本問は普通の問題(帰納的問題)ではなく、トランズダクティブ問題と呼ばれるものでした。
解き方について
- 調べものを怠らなかったこと
基本的なタスクの性質などから、関連コンペ(今回の場合「Ventilator Pressure Competition」など)を見つけ、その上位解法を複数あたって参考にしました。また、頻繁にGoogle検索を行いました。
例えば、Wonho Song氏らの3位解法は、マルチタスク学習のアイデアを与えてくれました。 - 実験を怠らなかったこと
これは自分内比較でしかありませんが、昨年JOAIに参加したときと比べて、今回ははるかに多くの実験を行いました。
どこかで見かけたコメントの受け売りになりますが、試したアプローチがすぐにうまくいかなくても、「うまくいかないはずがない」と思えるアプローチの時は、細かいところを色々と変えたり、実装の仕方が誤っていないか確かめたりして、しばらく諦めずに実験し続けることも重要かと思います。 - アイデアをまとめるメモを作ったこと
ただの裏紙ですが、何か試してみたいアプローチを思いついたり、実験の結果を書き留めたりしたときに重宝しました。これなしではどうなっていたか想像できません。
もっとも、あまりグチャグチャだったせいで、実験が成功したことを記録するのを忘れてしまったので、これはまずかったと思います(このため、先述の通り時間マスキングの実装を忘れた)。
まずかった点
- モデルの多様性が乏しかったこと
いくら元のモデルがかなり強いからといって、たった2つでは損であったと思います。より良いモデルを見つけることばかりに苦心して、性質の全く異なるモデルを色々混ぜてみることの強さを忘れていました。
実験すらしていないというのは、非常にまずいことでした。 - 普通にCVを使えばよいものを、MCCVにしてしまったこと
先述の通り、気の迷いです。もっとも、性能自体の顕著な低下にはつながっていないと思います。
あとがき
2度目にして最後のJOAIとなりましたが、昨年より大いに成長できたと感じています。
選抜枠5位から3位になったという相対評価的な伸びもよいですが、何より、知見とその集め方、実験の方法といった、今後のコンペや実際の問題解決でそのまま応用できる力がついていることが最もうれしいところです。
また、AIに楽しく向き合ったこの1年間で、将来の進路も明確に思い描くことができるようになりました。海外の大学で、もっと様々な人から教わり、もっと様々なデータを使ってAIを学びたいと思っています。
末筆ながら、これまで大会と選抜育成プログラムを通じて、私を大きく成長させ、新たな世界にいざなってくださったJOAI委員会のみなさまと、私を肩の上に乗せてくれた巨人である、Kaggleコミュニティのみなさまに、この上ない感謝を申し上げます。
追記
簡単な実験でアンサンブルの多様性を上げてみたところ、性能の改善が見られました。
次の条件でアンサンブルモデルを構築し、競技中最良であった予測とブレンドすると、Private LB は0.6935まで改善しました(0.0035の低下)。全体2位にあたる成績です。
- 1D CNN → BiGRU → Self-Attention ブランチの追加
- Non-causal TCN ブランチの追加
- LightGBMブランチの追加
- MLPブランチの追加
- 行レベル予測ブランチ(LightGBMおよびMLP)には追加の特徴量(前後ラグ、左右の和差など)を追加
- 脳活動データのマウスごとの標準化(前処理)の追加
- Ridge回帰による加重平均(スタッキング)
特段のハイパーパラメータ実験は行わず、直感のままに設定しましたが、十分有意な性能改善が得られました。
将来的には、分類モデルやGNNモデルなどを追加してさらに多様なアンサンブルを試みたいと思います。

