見出し画像

🏺【年内ラスト】Vesuvius Surface Detectionで“沼→0.467”まで駆け上がった話(そして次に0.47台を取りに行く設計図)

3日前、Public Scoreは 0.235
「これ、何か根本からズレてない?」と疑いながらも、手を動かして、試して、壊して、戻して。
昨日、ようやく 0.467 まで到達しました。

年内最後の投稿なので、今回は「気合い」でまとめます。
何が効いたのか/なぜ効いたのか/次に何をするのかを、技術的に腹落ちする形で書き残します。


1. 0.235 → 0.467:一番効いたのは「encoder刷新」だった

結論から言うと、スコアが跳ねた最大要因はこれです。

encoder="resnet34" → "timm-efficientnet-b3" に変更

3日前に0.23台で迷走していたときは、改善の方向性が見えづらかった。
でもB3に変えた瞬間、景色が変わった。これは偶然じゃなくて、セグメンテーション(特に2.5D)の構造と相性がいい。

なぜ EfficientNet-B3 が効きやすいのか(技術的整理)

Vesuviusのsurfaceは、輪郭がくっきりしているわけじゃない。
薄いテクスチャ差、局所の濃淡、かすれ…そういう“微妙な違い”を拾いに行くタスクです。

UNet/UNet++は、encoderが吐き出す各stageの特徴(浅いエッジ〜深い文脈)をdecoderで再構成する。
つまり encoderの特徴品質がそのままスコアに乗る

ResNet34でも動くけど、B3は

  • 特徴表現がリッチになりやすい

  • 同解像度帯で拾えるテクスチャ情報が増える

  • 2.5D(複数スライスch)の統合が強く出やすい

結果として、土台が一段上がる
今回のジャンプは、まさにそれだった。


2. 「突然スコアが落ちる」現象も体験した(0.467 → 0.452)

0.467まで来たあと、直近提出が 0.452 に落ちました。
この -0.015 は、モデルが弱いというより 推論パイプラインのどこかが壊れたサインになりやすい。

セグは特に、“盛った工夫”が逆に刺さって死ぬことがある。

典型的に落ちる原因(今回意識したチェックポイント)

  • 閾値がズレた(0.50固定の罠)

  • 後処理が強すぎる(MIN_SIZEやdilationで細いsurfaceが消える)

  • アンサンブルの平均方式ミス(logits平均↔︎prob平均、sigmoid二重)

  • TTAの入れ方(flip戻しミス/効果薄なのに時間だけ増える)

  • 正規化やsliceの取り方がtrainとズレた

この段階で痛感したのはこれ。

スコアを上げる前に、まず“壊れてない推論”を固定するべき


3. 0.46台の戦い方:この帯は「0.003で順位が動く」

この順位帯、スコア密度が高い。
0.470(+0.003)、0.474(+0.007)、0.477(+0.010)…と、少しの改善で順位が動く。

だからここからは、アーキ刷新より

  • 推論の再現性

  • 閾値最適化

  • 後処理の最適化

  • アンサンブルの安定化

が、最短で効く。


4. 推論を“壊さずに”伸ばす技術:まずは3手で切り分ける

セグは、いきなり全部盛るとどれが効いたかわからない。
提出枠も有限なので、「切り分けて当てる」のが速い。

Step 1:まず“素の推論”に戻す(安全確認)

  • TTA:OFF

  • 後処理:OFF(MIN_SIZE=0 / DILATE=0)

  • 閾値:0.50(仮)

  • アンサンブル:等重み(まず0.5/0.5)

目的はスコアUPじゃない。
パイプラインが壊れてないかを確認する。

Step 2:閾値をOOFで最適化(この帯の最強の拾い方)

0.50固定は損しやすい。
OOFで最適閾値を拾って固定すると、+0.003〜0.01が普通に出る。

Step 3:後処理は“弱く入れて”スイープ

  • まず MIN_SIZE のみ(0, 50, 100, 150, 200)

  • dilationは最後(入れるなら1から)

「見た目が綺麗」=「高スコア」ではない。
surfaceは細い構造になりやすいので、MIN_SIZE強すぎは普通にFNを増やす。


5. アンサンブルは “logits平均” が安定(ここ重要)

セグのアンサンブルで事故りやすいのが「平均の場所」。
prob平均でも動くけど、モデル間キャリブレーション差があると不利になりやすい。

経験上、最も安定するのは:

logitsを重み付き平均 → sigmoidは最後に1回

これは“壊れにくい”。


6. 次の予定:B3で堅く伸ばしつつ、B0を「役割」で使う

ここからの作戦は2軸。

軸A:現行EffB3でアンサンブルを詰めて0.47台へ

伸びやすい順にいくと:

  1. foldモデルのlogits平均(等重み)

  2. アンサンブル後に閾値をOOFで再最適化

  3. seed違いを追加して 2→3→5本へ

  4. checkpoint averaging(EMA/SWA等)でブレを減らす

この順で、スコアを落とさず上積みを狙う。

軸B:EffB0は「単体で勝つ」より“多様性枠”として使う

B0は軽い。だからこそできることがある。

  • img_size を 320/384 に上げる

  • num_slices を増やす(9/11など)

  • それでB3と違う予測傾向を作って、アンサンブルで得する

B0は「B3の下位互換」になりがちだけど、
設計を変えて予測のズレを作れれば強い味方になる。


7. タイムアウト回避の優先度(精度を落としにくい順)

時間制限は、このコンペの“現実”。

  • 最優先:GPUを必ず使う(Accelerator Noneは詰み)

  • 次:TTAは基本OFF(時間増が大きい割に効かないことがある)

  • 次:BATCH_Z を上げる(精度影響ゼロの高速化)

  • 最後:Z_INFER_STRIDE=2(速いが精度落ちやすいので最終手段)


まとめ:年内の結論は「土台を上げて、壊さずに詰める」

この数日で一番大きかった学びはこれです。

  • 土台(encoder)が弱いと、詰めても伸びない

  • 推論パイプラインが不安定だと、盛るほど死ぬ

  • 0.46〜0.47台は、モデル刷新より 閾値・後処理・アンサンブルが効く

ResNet34→EffB3で土台が上がった。
ここからは、壊れない推論を固定して、堅く0.47台を取りに行く。

年内ラスト、ここまで積み上げた。
年明けは、アンサンブルを完成させてもう一段上へ。


もし同じ帯で沼っている人がいたら、まず言いたいのはこれ。

「盛る」より先に「壊れてない」推論を作る。
そして閾値をOOFで決める。

この2つだけで、たぶん世界が変わります。


スキ・フォローで応援いただけると励みになります!
他にも「AI×仕事術」の具体的な活用ノウハウを発信しています。

👉 Threads: @code.hachi
👉 Instagram: @code.hachi

いいなと思ったら応援しよう!