初心者HMSコンペ
こんにちは。今回はkaggleで開催されていたHMSコンペで奇跡的に入賞したため、その時用いた解法を(自分が振り返るために)まとめたいと思います。
初めて獲得したメダルだったので嬉しかったです。モンストで初めてイザナミ倒した時の気持ち思い出しました。

自己紹介
B4→院進予定
学部:情報系
研究室:数理統計
研究ではほとんど理論をやっていて機械学習の知識は皆無
情報系ですがプログラミングがとにかく苦手
自分の基本的なkaggle戦略として
プログラミングが苦手なのであたりまえにフルスクラッチはできません。なので公開ノートブック、ディスカッションを見てよさそうなモデル、解法をいくつかピックアップします。
そのなかで自分がすこしいじれそうな所は某AIに聞きながら触る(なにも触らずそのままなんてこともざらにあります、というか大半がそう)
いろんな組み合わせでアンサンブルしてsubmitします。そして祈ります。
こんな感じでやっています、参考にまで。
そしてこの記事は某AIと共著しています。楽しい感じで書いてと指示してあるのでテンションがおかしい場合がありますがご了承ください。
今回は脳波(EEG)データを使ったコンペでした。
正直、機械学習もEEGも全然詳しくない状態からのスタートでしたが、「こうやればいいのか~」とか「これが大事なんだ!」と気づくことが多くて面白かったです。
この記事では、データの扱い方、モデルの選び方、工夫したポイントを振り返りながら、初心者目線でお話ししていきます。これから似たようなテーマに挑戦してみたい方の参考になれば幸いです。
コンペ概要
このコンペでは、EEGデータを使って「発作」や「異常な脳活動」を分類するモデルを作ることが目標でした。具体的には、EEGデータをスペクトログラム(音の周波数みたいなものを可視化した画像)に変換して、それを機械学習モデルで分類するというタスクです。
対象となるラベルは以下の6種類でした:
Seizure(発作)
LPD(片側性周期性放電)
GPD(全般性周期性放電)
LRDA(片側性リズミカルデルタ活動)
GRDA(全般性リズミカルデルタ活動)
Other(その他)
データには「専門家の投票数」という情報が付いていて、これが信頼性の指標になっていました。「投票数が多いほどデータの質が良い」「その他に集中しているデータは質が低い可能性が高い」ということがポイントでした。この投票数をどう活かすかが、このコンペを攻略するカギだった気がします。知らんけど
データの分け方が大事だった
今回のコンペで「これ超重要だな」って気づいたのが、データの分け方。いや、これって当たり前っちゃ当たり前なんですけど、ちゃんとやるとめっちゃモデルの精度が上がりました。
で、何を基準に分けたかっていいますと、「専門家の投票数」です。専門家がみんな「これだ!」って一致してるデータ、つまり投票数が多いデータは、信頼度も高いっぽいんですよね。逆に、投票が少なかったり、「その他」に集中してるデータは、なんていうかちょっと怪しい感じ。これ、みんなで「これ分類しづらいね」って話してるようなもんですからね。
最終的に、こんな感じでデータを分けることにしました:
高品質データ:投票数が10票以上のもの
低品質データ:投票数が1〜9票のもの
こうやって分けたら、モデルが「いい感じのデータ」を覚えてくれるようになって、パフォーマンスがグッと良くなりました。
自分で学ぶモデルすごくない?
あと、かなり効いたのが「自己学習」ってやつです。これ、簡単に言うと「モデル自身がデータをラベリングして、どんどん自分で賢くなっていく」みたいな仕組みです。なんか、ちょっとAIっぽくてカッコいいですよね。
やり方は超シンプルで、「低品質データ」でも一応モデルに予測させて、その結果をまたモデルに教えてあげるんです。こうすると、普通なら捨てちゃうようなデータも有効活用できて、「あれ、このモデルけっこう賢くなってない?」ってなるんですよ。
手順はこんな感じ
初期トレーニング
高品質データだけを使ってモデルを最初にトレーニングしました。低品質データに擬似ラベルを付与
そのモデルで、低品質データに「擬似ラベル」を付けました。ラベルを改良
擬似ラベルと元のラベルを組み合わせて、新しいデータセットを作りました。さらに、高品質データの50%にも同じ処理をして、全体のバランスを調整しました。改良したデータで再トレーニング
改良後のデータセットを使って、モデルを再びトレーニング。ファインチューニング
最後に元の高品質データを使って微調整して完成です。
最初は「これで本当に精度が上がるの?」と思いましたが、やってみたら意外と効果的でした。データを「ただ使う」だけじゃなくて、「育てる」感じですね。
これでモデルがぐんぐん成長して、最終的にはめっちゃいいスコアを叩き出してくれました。いやー、モデルって育てると愛着湧きますね。知らんけど
新しいスペクトログラムで特徴を補足
EEGデータはそのままだと扱いにくいので、スペクトログラムという形式に変換して使います。ただ、コンペ提供の10分間のスペクトログラムだけでは情報が足りない感じがしたので、ちょっとズームインしたデータも作ってみました。
作った3種類のスペクトログラム
10分間のスペクトログラム
全体の流れを見る用です。50秒間のスペクトログラム
少しズームインして、特徴を細かく見られるようにしました。10秒間のスペクトログラム
さらにズームインして、局所的な動きをキャプチャ。
これらを組み合わせて1つの512×512ピクセルの画像にまとめました。こうすることで、「全体的なパターン」と「細かい特徴」の両方をモデルに伝えられるようになりました。個人的には、この工夫がモデルの精度向上にかなり効いたと思います。
使用したモデルについて
今回使ったモデルは EfficientNetB0 と ResNet です。この2つは機械学習の世界ではわりと有名なモデルみたいなんですが、僕にとっては「なんとなく良さそうだから使ってみた」くらいのスタートでした。それでも実際に試してみたら、それぞれに強みがあることが分かりました。
EfficientNetB0
EfficientNetB0は、モデルが「軽い」=計算が速いのが特徴です。データがそんなに多くなくても、ちゃんといい結果を出してくれるところが便利でした。EEGみたいに、特徴が見えにくいデータでもちゃんと動いてくれるので、「これ使っとけば安心かな?」という感じで採用しました。
ResNet
ResNetは、もう少し「深く」データを学習するのが得意なモデルです。簡単に言うと、「もっと複雑なパターンを見つけられるよ」という感じ。EfficientNetB0だけでは拾えなかった細かい特徴も、ResNetがカバーしてくれるかなと思って組み合わせました。
どうしてこの2つを使ったの?
EfficientNetB0は軽くて速いので、全体の流れを見るのに向いています。一方で、ResNetは細かいところまでしっかり見られるので、2つを使い分けるとちょうど良かったんです。結果として、「簡単なパターンも複雑なパターンもどっちも拾える」バランスの取れた組み合わせになりました。
振り返り
今回のコンペで学んだことを振り返ってみると、「データをどう扱うか」と「モデルをどう選ぶか」がすごく大事だなと思いました。特に印象に残ったポイントをまとめます。
データの質を見極めるのが超大事
今回、専門家の投票数を基準にデータを分けて、高品質なデータだけを最初に使うようにしました。このちょっとした工夫だけで、モデルがしっかり学べるようになって、結果も良くなりました。「データの質を見極めるだけでこんなに変わるんだな」と驚きました。
自己ラベリングが案外いい感じ
低品質なデータをそのまま捨てずに、モデルが自分でラベルを予測して、その結果をまたモデルに教える「自己ラベリング」を試しました。正直、「これで本当に精度上がるの?」と思っていたんですが、やってみると意外と良い結果に。データを「使う」だけじゃなくて、「改善していく」って考え方が面白かったです。
モデルは状況に合わせて使い分ける
EfficientNetB0とResNetを組み合わせたことで、速さと正確さのバランスが取れました。でも、次回はまた違うモデルや工夫を試してみたいと思います。例えば、データの順番や時間の流れをもっと考慮できるモデルを使うと、さらに良くなるかもしれません。
失敗しながら学ぶのが楽しい
正直、最初は「これで合ってるのかな?」と思いながら進めていました。でも、いろいろ試して結果が少しずつ良くなるのを見ると、「やってみて良かったな」と思えました。小さな工夫でも結果に繋がると達成感がありますね。
今回の取り組みを通じて、機械学習の面白さを少しずつ感じられるようになりました。まだまだ分からないことだらけですが、これからも試行錯誤を重ねて、もっと成長していきたいと思います!(kaggle三ヶ月触っていません)
(数理統計の方が楽しいです)