Day 125 — ソロGold戦略 — Phase4×Phase5武器庫を統合した個人参加Gold圏内到達フレームワーク

2026-08-15 紫 / Phase 5(本日でPhase5完全終了 20/20) 戦略→コーディング モデル多様性 / CV-LBギャップ監視 / 最終2サブミット選定

📚 背景知識(読んでから問題へ)

🧰
Day071〜109(Phase4)で「GBDT・アンサンブル・Optuna・SHAP」というTabularコンペの武器を、Day110〜124(Phase5)で「PyTorch・CNN/転移学習・LSTM/Prophet/TFT・マルチモーダル融合」というディープラーニングの武器を積み上げてきました。今日はPhase5最終テーマとして、この2つの武器庫を1人で・チームを組まずに使い切り、Goldメダル圏内に入るための意思決定フレームワークに統合します。
🧑‍🤝‍🧑
なぜ「ソロ」が特別な論点になるのか: チームを組む一番のメリットは、メンバーごとに得意分野が異なるモデルを持ち寄れること。GBDT担当・NLP担当・CV担当が別々に作ったモデルは誤差の相関が低くなりやすく、アンサンブル時のスコア向上幅が大きい。ソロ参加者にはこの「他人の視点」がない。代わりに自分1人で「GBDT視点」と「DL視点」の両方を意図的に作ることで、チームの多様性を疑似的に再現する。これが今日の核心。チームを組まず1人でGoldを獲得することは"ソロGold"と呼ばれ、分業した場合以上に実力の証明として重んじられる。
参加チーム数Bronze圏内の目安Silver圏内の目安Gold圏内の目安
〜99チーム上位40%上位20%上位10%程度
100〜249チーム上位40%上位20%上位10チーム程度
250〜999チーム上位100チーム上位50チーム上位10〜数十チーム(上位2〜3%程度)
1000チーム以上上位10%上位5%上位1%未満

正式な閾値はKaggle公式Progression Systemページで確認する必要があるが、「Goldはチーム数が多いほど絶対人数ベースの狭き門になる」という傾向は共通している。

🧭
ソロGoldを狙うための4つの原則
①モデル多様性でチーム不在を補う: GBDT(LightGBM/XGBoost/CatBoost)とNN(Phase5のCNN/LSTM/マルチモーダル)は誤差の出方が異なりやすく、この2系統を仕込むことがチームの代わりになる
②CVを信じ、Public LBに振り回されない: CV-LBギャップ(Day101のNestedCV)が拡大していたら危険信号
③時間配分は「安い打ち手を先に」: 特徴量エンジニアリングやCV設計の見直しはモデル追加より低コスト
④最終サブミットは2つしか選べない: CVベスト1つ+CVとの乖離が小さいLBベスト1つが定石

🎯 問題

架空のKaggleコンペ「Retail Demand & Anomaly 2026」(店舗×商品ごとの日次需要予測、評価指標はSMAPE(小さいほど良い))に、あなたはチームを組まずソロで参加しています。

項目
データ形式数値/カテゴリ特徴量 + 180日分の時系列 + 商品説明文(テキスト)
評価指標SMAPE(小さいほど良い)
参加チーム数640チーム
コンペ残り日数18日
Gold / Silver / Bronze 圏内ライン上位20位 / 64位 / 128位
現在のPublic LB順位54位 / 640(SMAPE=12.83)
現在のCV(5-fold GroupKFold)SMAPE=12.55
試行済み手法LightGBM単体、ラグ/ローリング特徴量、Optunaチューニング、5-fold CV
未使用モダリティ / モデル系統商品説明文(テキスト) / CatBoost・NN系
チェックポイントPublic LB順位CVスコアLBスコアギャップ(LB-CV)
3週間前210位13.9015.10+1.20
2週間前130位13.3014.25+0.95
1週間前88位12.8013.41+0.61
今日54位12.5512.83+0.28
1
武器の優先順位付け(戦略): Phase4(GBDT・アンサンブル・Optuna・SHAP)とPhase5(PyTorch・CV・時系列DL・マルチモーダル)の武器庫から、残り18日でGold圏内(あと34人分の順位改善)を狙うために投入すべき打ち手を優先度順に5つ挙げ、狙いを一言で説明する
2
意思決定のコード化(コーディング・メイン): 次に取るべき行動を提案するrecommend_solo_gold_final_push(...)を実装する。残り日数3日以下は新規モデル禁止、未使用モダリティ×残り7日以上で多様化提案、GBDT単系統ならモデル系統の追加提案、CV-LBギャップ拡大時は警告、最終2サブミットの選び方は必須で含める、の5ルールを満たす
3
結果を踏まえた次の一手(戦略・分析): 順位・CV-LBギャップの推移表から関数の提案が合理的かを検証し、「今日から18日間、何をどの順で行うか」を結論づける

📊 順位とメダル圏の可視化(640チーム中の現在地)

順位トラック(1位〜150位を拡大表示、151〜640位は右端に圧縮) Gold 1-20位 Silver 21-64位 Bronze 65-128位 129〜640位(圏外) 現在54位 Gold圏まであと34人分 640チーム中、現在は Silver 圏内の54位。Gold(20位)まで34人分の差を残り18日で詰める必要がある。

💡 ヒント

ヒント1方向性

新しい技術要素は登場しません。「Phase4とPhase5でバラバラに学んだ武器を、限られた残り日数の中でどの順番で投入するか」を、Day120と同じ「条件分岐によるルール化」で表現する回です。タスク2では「モダリティの多様性」と「モデル系統の多様性」という2種類の多様性を区別して扱う点がポイントです。

ヒント2アプローチ
  • CV-LBギャップの推移が「拡大傾向」か「縮小傾向」かは、直近4値の差分(gap[i] - gap[i-1])がすべて負(縮小し続けている)かどうかで機械的に判定できる
  • 「モダリティの多様性」はmodalities_availablemodalities_usedの差分、「モデル系統の多様性」はtried_methodsにGBDT系以外(catboost/neural_net等)が含まれるかで判定する。この2つは独立した別の判定軸
  • 順位差(current_rank - gold_rank_threshold)が大きいほど、小手先の調整ではなく特徴量エンジニアリングのような根本的な打ち手をリストの先頭付近に置く
  • 最終2サブミットの提案は、状況にかかわらず必ず返り値の最後に含める
ヒント3コード骨格
def recommend_solo_gold_final_push(
    days_left, current_rank, gold_rank_threshold,
    cv_score, lb_score, cv_lb_gap_history,
    tried_methods, modalities_available, modalities_used,
):
    actions = []
    rank_gap = current_rank - gold_rank_threshold

    # 縮小/拡大傾向の判定(直近4値の差分がすべて負なら縮小傾向)
    gap_diffs = [cv_lb_gap_history[i] - cv_lb_gap_history[i-1] for i in range(1, len(cv_lb_gap_history))]
    gap_widening = any(d > 0 for d in gap_diffs)

    # ... 順位差が大きければ特徴量エンジニアリングを優先、
    # 未使用モダリティ×残り日数7日以上でモデル多様性を追加、
    # GBDT単系統ならモデル系統を追加、残り3日以下は新規禁止 ...

    actions.append({"action": "select_final_2_submissions", "priority": "must"})
    return actions

模範解答

タスク1: 優先度順の打ち手リスト(残り18日・Gold圏まで34人分)

  1. 1
    特徴量エンジニアリングの深掘り(Phase3-4) — 34人分の差は小手先のチューニングでは埋まりにくい。ラグ・ローリング統計量の粒度追加や店舗×商品の交互作用特徴量を優先する
  2. 2
    テキストモダリティ用のマルチモーダルNN追加(Day124) — 未活用の商品説明文をIntermediate Fusionで取り込み、GBDT単体では拾えない誤差パターンを補う
  3. 3
    CatBoostまたはNN系モデルの追加(Phase4/5) — LightGBM単系統から脱し、アンサンブルの前提となる「誤差の相関が低い2つ目のモデル」を用意する
  4. 4
    アンサンブル重みの最適化(Day102 Averaging) — 複数モデルが揃った段階でRank Average/重み最適化を行う
  5. 5
    残り3日を切ったらシード平均のみに専念し、最終2サブミットを選定 — 新規モデル追加を凍結し、CVベスト+CVとの乖離が小さいLBベストを選ぶ
🧩
設計思想: 「根本的な打ち手(特徴量)→多様性の確保(モダリティ・モデル系統)→仕上げ(アンサンブル・最終選定)」という順で並べている。チームがいない分、多様性の確保を意図的にスケジュールに組み込む必要がある。

タスク2: recommend_solo_gold_final_push の実装

def recommend_solo_gold_final_push(
    days_left: int,
    current_rank: int,
    gold_rank_threshold: int,
    cv_score: float,
    lb_score: float,
    cv_lb_gap_history: list[float],
    tried_methods: list[str],
    modalities_available: list[str],
    modalities_used: list[str],
) -> list[dict]:
    """残り日数・現状のスコア/順位・試行済み手法から、ソロGoldに向けた次の一手を優先度順に返す。"""
    actions = []
    rank_gap = current_rank - gold_rank_threshold

    # --- CV-LBギャップの傾向判定(直近の差分がすべて負なら縮小傾向) ---
    gap_diffs = [cv_lb_gap_history[i] - cv_lb_gap_history[i - 1] for i in range(1, len(cv_lb_gap_history))]
    gap_widening = any(d > 0 for d in gap_diffs)

    # --- ルール1: 残り日数3日以下なら新規モデル追加を禁止 ---
    freeze_new_models = days_left <= 3

    # --- 順位差が大きい場合は根本的な打ち手を最優先に ---
    if rank_gap > 20 and not freeze_new_models:
        actions.append({
            "action": "feature_engineering_deep_dive",
            "priority": "high",
            "reason": f"Gold圏まで{rank_gap}人分の差があり、特徴量エンジニアリング(Phase3-4)を優先する",
        })

    # --- ルール2: 未使用モダリティ×残り日数7日以上でモデル多様性を追加 ---
    unused_modalities = [m for m in modalities_available if m not in modalities_used]
    if unused_modalities and days_left >= 7 and not freeze_new_models:
        target = unused_modalities[0]
        actions.append({
            "action": f"add_multimodal_branch_for_{target}",
            "priority": "high",
            "reason": f"「{target}」が未活用。Day124のIntermediate Fusionでモデル多様性を補う",
        })

    # --- ルール3: GBDT単系統のみならモデル系統を追加 ---
    gbdt_family = {"lightgbm", "xgboost", "catboost"}
    nn_family = {"neural_net", "cnn", "lstm", "tft", "multimodal_nn"}
    has_gbdt_diversity = len(gbdt_family & set(tried_methods)) >= 2
    has_nn = len(nn_family & set(tried_methods)) >= 1
    if not (has_gbdt_diversity or has_nn) and not freeze_new_models:
        actions.append({
            "action": "add_second_model_family",
            "priority": "medium",
            "reason": "LightGBM単系統のみ。CatBoardかNN系を追加し誤差相関の低いモデルを揃える",
        })

    # --- ルール4: CV-LBギャップが拡大傾向なら警告(今回は縮小傾向のため発火しない) ---
    if gap_widening:
        actions.append({
            "action": "distrust_public_lb",
            "priority": "high",
            "reason": "CV-LBギャップ拡大。Public LBよりCVスコアを重視する",
        })

    if freeze_new_models:
        actions.append({
            "action": "seed_averaging_and_weight_tuning_only",
            "priority": "high",
            "reason": "残り日数僅少。新規モデルはリスクが高いためシード平均・重み最適化のみ",
        })

    # --- ルール5: 最終2サブミットの選び方は必ず提案 ---
    actions.append({
        "action": "select_final_2_submissions",
        "priority": "must",
        "reason": "1つ目はCVベスト、2つ目はCVとの乖離が小さいLBベストを選ぶ",
    })
    return actions


# --- 動作確認: 「Retail Demand & Anomaly 2026」の現在の状況 ---
plan = recommend_solo_gold_final_push(
    days_left=18, current_rank=54, gold_rank_threshold=20,
    cv_score=12.55, lb_score=12.83,
    cv_lb_gap_history=[1.20, 0.95, 0.61, 0.28],
    tried_methods=["lightgbm", "feature_engineering_lag_rolling", "optuna_tuning"],
    modalities_available=["tabular", "timeseries", "text"],
    modalities_used=["tabular", "timeseries"],
)
for i, a in enumerate(plan, 1):
    print(f"{i}. [{a['priority']}] {a['action']}")
# 出力例:
# 1. [high] feature_engineering_deep_dive
# 2. [high] add_multimodal_branch_for_text
# 3. [medium] add_second_model_family
# 4. [must] select_final_2_submissions
# (CV-LBギャップは1.20→0.95→0.61→0.28と一貫して縮小 → distrust_public_lb は発火しない)

タスク3: 順位・ギャップ推移の分析と最終結論

チェックポイントLB順位ギャップ解釈
3週間前210位+1.20ギャップ大=初期のCV設計がまだ粗い段階
2週間前130位+0.95特徴量追加でCVも改善、ギャップも縮小
1週間前88位+0.61Optunaチューニングの効果、さらに縮小
今日54位+0.28ギャップ1/4程度に縮小=過学習ではなく素直な改善
🏁
結論: CV-LBギャップの一貫した縮小は「Public LBの改善が過学習ではなく正しくCVに裏付けられている」良いサイン。distrust_public_lbが発火しなかった判定は妥当。今日から18日間は、①特徴量エンジニアリングの深掘り、②テキストモダリティのマルチモーダルNN投入、③CatBoost/NN系の追加でモデル多様性を確保、という順で進め、残り3日を切った時点で新規モデル追加を完全凍結し、シード平均と最終2サブミットの選定に専念するのが合理的。

📉 CV-LBギャップの推移(縮小傾向=良い兆候)

SMAPE(実線: CV / 破線: Public LB、小さいほど良い) 15.2 13.9 12.5 3週間前 2週間前 1週間前 今日 gap +1.20 +0.95 +0.61 +0.28 ▼縮小 → CVもLBもともに改善し、ギャップは一貫して縮小 = 過学習ではなく健全な改善傾向
🔍
ギャップの絶対値そのものより「拡大しているか縮小しているか」という傾きが危険信号として重要。今回は一貫して縮小しているため、distrust_public_lbは発火せず、Public LBの改善をある程度信頼してよい状況と判断できる。

🪜 Step-by-Step 解説

1「モダリティの多様性」と「モデル系統の多様性」を分けて判定する

unused_modalities = [m for m in modalities_available if m not in modalities_used]
...
has_gbdt_diversity = len(gbdt_family & set(tried_methods)) >= 2
has_nn = len(nn_family & set(tried_methods)) >= 1
🔍
なぜこうするか: 「テキストを使っていない」ことと「モデル系統が1つしかない」ことは別問題。前者は入力データの多様性、後者は学習アルゴリズムの多様性。混同すると「テキストを足したLightGBM」だけで満足し、NNが得意な非線形な交互作用を見逃す。2つの軸を別々の条件式で判定することで混同を防ぐ。

2CV-LBギャップの「傾向」を差分の符号で判定する

gap_diffs = [cv_lb_gap_history[i] - cv_lb_gap_history[i - 1] for i in range(1, len(cv_lb_gap_history))]
gap_widening = any(d > 0 for d in gap_diffs)
📐
なぜこうするか: ギャップが常に一定の正の値で安定していれば単なる分布の系統的ズレだが、回を追うごとに拡大しているなら、モデルがPublic LBという一部のtestデータに過剰適合し始めている可能性が高い。今日のデータは一貫して縮小しているため警告は発火しない。

3残り日数3日以下では新規モデルの提案を機械的に止める

freeze_new_models = days_left <= 3
if rank_gap > 20 and not freeze_new_models:
    ...
なぜこうするか: 終盤に新モデルを追加すると十分な検証時間が取れず、バグや過学習に気づけないまま最終サブミットを迎えるリスクが高い。freeze_new_modelsという1つのフラグで、複数の提案ルールすべてに一貫して「終盤は守りに入る」方針を反映できる。

4「最終2サブミットの選び方」だけは条件によらず必ず返す

actions.append({"action": "select_final_2_submissions", "priority": "must", ...})
⚖️
なぜこうするか: 残り日数がどれだけあっても、最終的に2つのサブミットを選ぶ判断は必ず発生する。早い段階から「CVベスト1つ+CVとの乖離が小さいLBベスト1つ」という方針を意識しておくことで、土壇場で両方ともPublic LBベストを選んでしまう(=シェイクダウンで共倒れする)事故を防げる。

🧮 数学・統計の補足(文系向け)

🎲
なぜ「誤差の相関が低いモデルを混ぜる」とスコアが上がるのか——チームの代わりを数式で理解する:

n個の独立な推定値(誤差の分散はどれも同じσ²、誤差同士の相関係数ρ)を単純平均したときの分散は、次の式で近似できます。

Var(平均) = ρσ² + (1-ρ)σ²/n

ρ=0(誤差が完全に無相関)なら、モデルを増やすほど分散はσ²/nまで小さくなります。しかしρが1に近い(=どのモデルも似た間違え方をする)と、いくら増やしてもρσ²という下限から下がりません。LightGBM・XGBoost・CatBoostを3つ並べても「木を分割して予測する」という同じ仕組みのためρは高くなりがちですが、NN(Day111-124)は決定木と全く異なる仕組みで学習するためρが下がりやすく、アンサンブルの分散低減効果が大きくなります。チームの一番の価値は「ρの低い誤差源=他人の頭脳」を確保できることであり、ソロ参加者はGBDTとNNというアルゴリズムの系統を意図的に変えることで、これに近い効果を1人で再現しています。

🏆 Kaggleでの実践的な使い方

よく使われるコンペカテゴリ: ☑ 表形式データ(Tabular) / ☐ 自然言語処理(NLP) / ☐ 画像認識(CV) / ☑ 時系列(Time Series)

実例

🛒 M5 Forecasting

GBDT数個 + 簡易NNの終盤アンサンブル

上位ソロ参加者の多くが「LightGBM系のモデル数個 + 簡易的なNN(Day121のLSTM相当)」を最終盤でアンサンブルしており、今日学んだ「GBDT×NNの多様性戦略」の実例になっている。

実例

📈 Optiver Realized Volatility

CV-LBギャップを日次で記録

ソロGoldを獲得した参加者のsolution writeupでは「CV-LBギャップを毎日記録したスプレッドシート」を運用していたという報告が複数あり、今日のタスク3のような監視が実際に使われている。

応用

🏭 実務での応用

モデル多様性をリスク検知に転用

需要予測・異常検知でも「統計手法+GBDT+DL」を並行運用し、予測の乖離が大きいサンプルを人間が重点レビューする「モデル多様性をリスク検知に使う」考え方に転用できる。

⚠️ よくある誤解・ミス

誤解・ミスなぜ起こるか正しい理解
Public LBの順位が上がっていれば安心してよいと思う目に見える指標(順位)に安心感を覚えやすいPublic LBはtest全体のごく一部にすぎず、終了時にPrivate LBで大きく順位が変動するシェイクアップ/ダウンが起こりうる。常にCVとの乖離を確認する
モデルを増やすほどアンサンブル効果が大きくなると思う「数が多いほど良い」という直感誤差の相関が高いモデルを増やしても効果は頭打ちになる。GBDT×NNのように系統の異なるモデルを混ぜる必要がある
残り日数が少なくなってから慌てて新モデルを追加しようとする「まだ間に合うかも」という焦り検証不十分な新規モデルを終盤に混ぜるリスクの方が大きい。閾値をあらかじめ決め、機械的に止める判断が有効
最終2サブミットを両方ともPublic LBベストで選んでしまう「一番良いスコアを選べば安全」という直感同じ過学習傾向を持つ2つを選ぶとPrivate LBで共倒れする。必ず1つはCVベスト(LBに寄せていない安全な選択)を残す
ソロGoldは単に「運が良かっただけ」だと軽視するメダルの色だけを見て中身を評価しないソロGoldはモデル多様性・CV設計・時間配分のすべてを1人で判断し切った結果。分業できない分、意思決定の質がそのままスコアに直結する

🚀 次のステップ

  • 発展: recommend_solo_gold_final_pushに、Day107(SHAP)を使い「GBDTとNNのどちらが重視している特徴量が異なるか」を定量比較するcompare_feature_attribution(gbdt_shap, nn_gradient_importance)を追加してみましょう。2つのモデルが本当に異なる情報源を見ているかを検証でき、アンサンブルの多様性を数値で裏付けられます
  • 次回予告: Day 126 — Phase 6突入(Grandmaster)。本日でPhase 5(テーマ20/20)が完全に完了しました。次のPhase 6ではSOTA手法の追跡・論文実装から始まり、カスタムアーキテクチャ設計、Pseudo Labeling・TTAといった高度なアンサンブル、そして最終的に「Gold 3枚達成プラン」というコンペ戦略まで、Grandmaster到達に向けた最終段階に入ります

Phase 5 の学習マップ(全20テーマ・本日完了)

1-4 DL基礎(PyTorch) 完了
6-10 NLP(後日)
11-15 CV 完了
16-18 時系列 完了
19-20 マルチモーダル/ソロGold 完了

Day110〜125の16日間でDL基礎・CV・時系列DL・マルチモーダル・ソロGold戦略までを一通り学び、Phase5が完了した。次はPhase6(Grandmaster)へ進む。

📝 自己評価(解いた後に記入)

自分の回答・気づき・メモ: