Day 142 — コンペ戦略② — 探索と活用のトレードオフ(CVが伸び悩んだときの意思決定)

2026-09-01 金 / Phase 6(Grandmaster、テーマ4群 の 2日目) 戦略→コーディング 探索(EXPLORE) vs 活用(EXPLOIT) / 多腕バンディット / 適応的ルール

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

🔁
Day141ではコンペ終了時点の意思決定(最終提出2枠の選び方)を扱った。今日はコンペ進行中の意思決定を扱う。CVスコアが伸び悩んだとき、新しい特徴量を試す「探索(EXPLORE)」に踏み切るか、アンサンブルの微調整に留める「活用(EXPLOIT)」を続けるか — この判断を、精神論ではなくシミュレーションで比較する。
🎰
今日の核心 — これは「多腕バンディット問題」である: 未知の当たりやすさを持つ複数の選択肢から、限られた試行回数(提出予算)の中でどう配分するか、という問題は Kaggle に限らず広告A/Bテストや臨床試験でも現れる普遍的な構造。よくある素朴な戦略「前半探索・後半活用」という固定スケジュールに対し、「直近の活用がN回連続で逓減しきったら探索に切り替える」という適応的ルールが本当に優れているかを実測で確かめる。
キーワード直感的な説明
探索(EXPLORE)未知の新しいアイデア(特徴量・モデル)を試す行動。当たれば大きな改善、外れれば提出を消費するだけで終わる
活用(EXPLOIT)既知の枠組みの中でパラメータ・重みを微調整する行動。改善幅は小さいが確実。同じ枠組みを続けるほど逓減する
多腕バンディット問題未知の当たりやすさを持つ複数の選択肢から、限られた試行回数で最大の報酬を得るための配分を決める意思決定問題
逓減(diminishing returns)同じ種類の改善を繰り返すほど、1回あたりの効果が小さくなっていくこと
適応的ルール(adaptive policy)あらかじめ決めたスケジュールではなく、観測された結果に応じてその都度、次の行動を決める戦略

🎯 問題

タスクA(戦略・記述)

検証フェーズ(残り提出予算20回)に入ったところ、直近5回の提出でCVスコアがほぼ横ばい(停滞)になっているとする。次の1回で「探索」と「活用」のどちらを選ぶかを、以下3点を踏まえて3〜4文で説明する。

1
「停滞」というシグナルは、探索と活用のどちらを後押しする材料になるか
2
残りの提出予算が少なくなるほど(残り20回 vs 残り3回)、どちらを優先すべきか、その理由
3
探索が「外れた」場合に失うものは何か(提出回数以外の機会費用も考える)

タスクB(実装・シミュレーション)

項目値
残り提出予算N_STEPS = 20
開始CVスコアBASE = 0.800(停滞している状態)
探索の当たり確率・改善幅P_HIT = 0.20 / Uniform(0.003, 0.008)
活用の初回改善幅・逓減係数EXPLOIT_BASE_GAIN = 0.0015 / EXPLOIT_DECAY = 0.75
適応的ルールの切替閾値K_ADAPTIVE = 3(連続活用がこの回数で探索へ切替)
比較する戦略常に活用 / 常に探索 / 固定半々 / 適応的ルール
反復回数N_SIM = 20000
import numpy as np
rng = np.random.default_rng(0)

N_STEPS = 20
N_SIM = 20000
BASE = 0.800

P_HIT = 0.20
EXPLORE_GAIN_LOW, EXPLORE_GAIN_HIGH = 0.003, 0.008

EXPLOIT_BASE_GAIN = 0.0015
EXPLOIT_DECAY = 0.75

K_ADAPTIVE = 3
B1
状態はcv(現在のCVスコア)とexploit_streak(連続で活用した回数)の2つで表現する
B2
戦略A「常に活用」B「常に探索」C「固定半々」D「適応的ルール」を実装し、それぞれの最終cvを記録する
B3
N_SIM回繰り返し、平均最終cv・平均改善幅(gain)・平均探索回数・平均当たり回数・標準偏差を比較する
B4
(追加検証)K_ADAPTIVEを1〜10で変化させ、平均改善幅と標準偏差の変化を調べる
B5
(考察): 適応的ルールは常に探索する戦略や固定半々より本当に優れていたか、探索回数・当たり回数・ばらつきを根拠に150字程度で結論づける

💡 ヒント

ヒント1方向性

タスクAは「停滞は今の枠組みの伸びしろが尽きたシグナル」と「残り予算が少ない終盤ではリスクを取りにくい」という2つの軸を分けて考えることがポイント。タスクBは「探索」と「活用」をそれぞれ確率的な報酬モデルとして表現し、固定スケジュールで割り振る場合と、観測された状態(連続活用回数)に応じて動的に切り替える場合とでどれだけ差が出るかを測るシミュレーション。活用の効果がEXPLOIT_DECAYで逓減する設計になっている点がポイント。

ヒント2アプローチ
  • 探索が当たるとexploit_streakは0にリセットされる点を忘れないこと(新しい特徴量の周辺には未チューニングの伸びしろが生まれる)
  • 戦略C「固定半々」はstep < N_STEPS // 2かどうかで探索/活用を切り替える
  • 戦略D「適応的ルール」はexploit_streak >= K_ADAPTIVEかどうかだけを見る。初手はexploit_streak=0なので必ず活用になる
  • 「探索を何回選んだか」「探索が何回当たったか」は別々のカウンタで集計する
  • rng.uniform(low, high)は一様分布サンプリング、rng.random() < P_HITは確率P_HITでの成功判定に使う
ヒント3コード骨格
def run_policy(policy, rng):
    cv = BASE
    exploit_streak = 0
    n_explore = 0
    n_hit = 0
    for step in range(N_STEPS):
        if policy == "always_exploit":
            action = "exploit"
        elif policy == "always_explore":
            action = "explore"
        elif policy == "fixed_half":
            action = "explore" if step < N_STEPS // 2 else "exploit"
        elif policy == "adaptive":
            action = "explore" if exploit_streak >= K_ADAPTIVE else "exploit"

        if action == "explore":
            n_explore += 1
            if rng.random() < P_HIT:
                cv += rng.uniform(EXPLORE_GAIN_LOW, EXPLORE_GAIN_HIGH)
                n_hit += 1
                exploit_streak = 0
            # 外れた場合はcv変化なし
        else:
            cv += EXPLOIT_BASE_GAIN * (EXPLOIT_DECAY ** exploit_streak)
            exploit_streak += 1

    return cv, n_explore, n_hit

✅ 模範解答

# --- 4戦略×N_SIM回のモンテカルロシミュレーション(ヒント3のrun_policyを実行) ---
policies = ["always_exploit", "always_explore", "fixed_half", "adaptive"]
results = {p: [] for p in policies}
n_explore_used = {p: [] for p in policies}
n_hit_used = {p: [] for p in policies}

for sim in range(N_SIM):
    for p in policies:
        cv, n_explore, n_hit = run_policy(p, rng)
        results[p].append(cv)
        n_explore_used[p].append(n_explore)
        n_hit_used[p].append(n_hit)

for p in policies:
    arr = np.array(results[p])
    gain = arr - BASE
    print(f"{p:16s} mean_gain={gain.mean():.5f}  std={arr.std():.5f}")

# ============ 実測結果 ============
# always_exploit   mean_final_cv=0.80598  mean_gain=0.00598  n_explore=0.00   n_hit=0.00   std=0.00000
# always_explore   mean_final_cv=0.82201  mean_gain=0.02201  n_explore=20.00  n_hit=4.00   std=0.01034
# fixed_half       mean_final_cv=0.81662  mean_gain=0.01662  n_explore=10.00  n_hit=1.99   std=0.00723
# adaptive         mean_final_cv=0.82257  mean_gain=0.02257  n_explore=11.10  n_hit=2.22   std=0.00781
🧭
タスクB-7: 考察 — 適応的ルール(戦略D、mean_gain=0.02257)は、常に探索する戦略B(0.02201)をわずかに上回り、固定半々の戦略C(0.01662)を明確に上回った。しかも使った探索回数は平均11.10回と戦略Bの20回よりずっと少ない。活用の効果が逓減する設計のもとでは「連続活用が3回に達したら探索に切り替える」ルールが、逓減しきったタイミングだけを捉えて探索に切り替える一方、活用がまだ伸びしろを残しているうちは無理に切り替えない、効率的な配分になっている。固定半々が劣るのは、実際にCVが停滞したかとは無関係に最初の10回を機械的に探索へ割り当て、逓減前の活用の伸びしろを使わずに終わってしまうためと考えられる。

📊 結果の可視化

戦略ごとの平均改善幅(gain = 最終CV − BASE) 0 .02257適応的ルール .02201常に探索 .01662固定半々 .00598常に活用 → 適応的ルールが最良。探索回数が少ない(11.10回)にもかかわらず常に探索(20回)をわずかに上回る

戦略ごとの平均探索回数(探索に使った提出回数 / 全20回中):

適応的ルール
11.10 / 20
常に探索
20.00 / 20
固定半々
10.00 / 20
常に活用
0.00 / 20

探索の「当たり」回数(平均): 適応的ルール 2.22回 / 常に探索 4.00回 / 固定半々 1.99回。少ない探索回数でも当たりを効率よく拾えている

戦略平均gain平均探索回数平均当たり回数std(リスク)
適応的ルール0.0225711.102.220.00781
常に探索0.0220120.004.000.01034
固定半々0.0166210.001.990.00723
常に活用0.005980.000.000.00000

🪜 Step-by-Step 解説

1なぜ「活用」に逓減を組み込むのか

cv += EXPLOIT_BASE_GAIN * (EXPLOIT_DECAY ** exploit_streak)
exploit_streak += 1
📉
なぜこうするか: 現実のアンサンブル重み調整も同じ枠組みでやり続けるほど「もう調整し尽くした」状態に近づき、1回あたりの改善幅は小さくなっていく。逓減がなければ「活用だけをずっと続ける」戦略が理論上いくらでも改善し続け、探索とのトレードオフというテーマ自体が成立しなくなる。EXPLOIT_DECAY ** exploit_streakという等比数列の形で「同じ行動を繰り返すほど価値が減る」直感をコードに落とし込んでいる。

2なぜ探索の当たりでexploit_streakをリセットするのか

if rng.random() < P_HIT:
    cv += rng.uniform(EXPLORE_GAIN_LOW, EXPLORE_GAIN_HIGH)
    n_hit += 1
    exploit_streak = 0
🔄
なぜこうするか: 新しい特徴量が見つかると、その特徴量をアンサンブルにどう組み込むかという新しい活用の伸びしろが生まれる。リセットしないと、探索で新しいアイデアが当たっても「もう活用し尽くした古い枠組み」のままシミュレーションが進み、探索の価値が過小評価されてしまう。「探索と活用は交互に相乗効果を生む」という現実の感覚がここに反映されている。

3なぜ適応的ルールが常に探索する戦略に勝てるのか

# always_explore  mean_gain=0.02201  mean_n_explore=20.00  mean_n_hit=4.00
# adaptive        mean_gain=0.02257  mean_n_explore=11.10  mean_n_hit=2.22
⚖️
なぜこうするか: 常に探索する戦略はP_HIT=0.20なので20回中平均4回しか当たらず、残り16回は提出を消費するだけで終わる。適応的ルールは活用の伸びしろが逓減しきった(exploit_streak>=3)タイミングだけ探索に切り替えるため、探索回数自体は少ない(平均11.10回)にもかかわらず、合間の活用フェーズで着実に小さな改善を積み重ねている。「探索の回数を増やせば良い」のではなく「稼げるうちは活用し、稼げなくなったら探索する」メリハリの方が効率的という結果になった。

🔁 追加検証(K_ADAPTIVEを変化させた場合)

適応的ルールの「何回連続で活用したら探索に切り替えるか」を決めるK_ADAPTIVEを1〜10まで変化させ、平均改善幅と標準偏差(リスク)がどう変化するかを確認した。

# K= 1  mean_gain=0.02371  std=0.00967
# K= 2  mean_gain=0.02348  std=0.00866
# K= 3  mean_gain=0.02254  std=0.00776   ← 今日のメイン設定
# K= 4  mean_gain=0.02137  std=0.00696
# K= 5  mean_gain=0.02010  std=0.00618
# K= 7  mean_gain=0.01761  std=0.00520
# K=10  mean_gain=0.01474  std=0.00361
K_ADAPTIVE を大きくするほど gain(緑) も std(橙) も小さくなる K=1 K=10 mean_gain std
🎚️
Kを小さくする(早めに探索へ切り替える)ほど平均改善幅は大きくなるが、同時に標準偏差(ばらつき・リスク)も大きくなる。探索は「当たれば大きいが外れる確率の方が高い」行動のため、探索比率を増やすほど「平均は良くなるが結果が安定しない」トレードオフが生まれる。Kの選び方自体が「どれだけリスクを取れるか」の選択であり、正解は1つに決まらない。予算に余裕がある序盤はKを小さくして積極的に探索し、予算が減る終盤はKを大きくして安定運用に寄せる、という発想がDay141の「終盤は安全策に寄せる」とも接続する。

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

🏃
「逓減」を、ランニングのタイム短縮の例えで理解する:

初心者がジョギングを始めると、最初の数週間はタイムがどんどん縮みます。しかしある程度速くなると、同じ練習を続けてもタイム短縮のペースはどんどん小さくなっていきます。今日の「活用」のEXPLOIT_DECAY ** exploit_streakはこれと同じ構造で、同じ種類の練習(アンサンブル調整)を繰り返すほど、1回あたりの伸びが指数関数的に小さくなっていくことを表しています。
➗
「等比数列の和」で、活用だけを続けた場合の理論上限を計算する:

活用だけをずっと続けた場合の合計改善幅は、次の等比数列の和で計算でき、実測mean_gain=0.00598と一致することが確認できます。
total = EXPLOIT_BASE_GAIN * sum(EXPLOIT_DECAY**i for i in range(N_STEPS))
      = 0.0015 * (1 - 0.75**20) / (1 - 0.75)
      = 0.0015 * 3.9873...
      = 0.005981...
「1回目は満額、2回目は0.75倍、3回目は0.75×0.75倍…」と効果が等比数列的に小さくなっていくとき、無限に続けても合計は無限に大きくならず、ある一定の値(EXPLOIT_BASE_GAIN / (1 - EXPLOIT_DECAY) = 0.006)に近づいて頭打ちになります。中学・高校で習う等比数列の和の公式が、そのままKaggleの「チューニングの限界」の説明に使えます。

🏆 Kaggleでの実践的な使い方

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

原則

🚦 停滞したら探索、まだ伸びるなら活用

状態依存の判断が固定スケジュールより効率的

「直近N回の提出でCVが改善しなかったら、次は毛色の違う特徴量を試す」という自分ルールを事前に決めておくと、感覚的な判断のブレを減らせる。

実例

⏳ 残り予算とKの関係

終盤は探索への切替を遅らせる

探索への切り替えを早める(Kを小さくする)ほど平均は良くなるがばらつきも増える。終盤で順位を安定させたい局面では、あえて活用寄りに倒す判断が合理的。

実務

🌱 探索の当たりは次の伸びしろを生む

見つけたらすぐ固定せず、周辺を再調整する

新しい特徴量やモデルを見つけたら、その周辺の重み・パラメータを改めてチューニングする余地があることを意識する。exploit_streakのリセットはこの相乗効果を表す。

⚠️ よくある誤解・ミス

誤解・ミスなぜ起こるか正しい理解
CVが停滞したら、とにかく新しいことを試すべきだと思い込む「停滞=行き詰まり」という直感的な焦り今日の実測では常に探索する戦略(mean_gain 0.02201)は適応的ルール(0.02257)にわずかに劣り、探索だけに頼ると外れの多さ(20回中平均16回が外れ)で提出を浪費する
探索の回数を増やせば増やすほど平均的な結果は良くなると思う「試行回数=改善のチャンス」という単純化適応的ルールは探索回数が平均11.10回と常に探索する戦略の20回より少ないのに、平均改善幅は上回った。回数より「稼げなくなったタイミングで切り替える」判断の質が重要
「前半探索・後半活用」という固定スケジュールは十分合理的だと思うカレンダー的な区切りは分かりやすく実践しやすい今日の実測で固定半々(mean_gain 0.01662)は適応的ルール(0.02257)や常に探索(0.02201)より明確に劣った。実際に停滞しているかを見ずに時期だけで判断すると、活用で稼げる伸びしろを無駄にする
リスクを気にせず、平均改善幅が一番良い戦略(小さいK)を常に選ぶべきだと思う平均値だけを見て判断してしまう追加検証の通り、平均改善幅が良い戦略ほど標準偏差(ばらつき)も大きい。残り予算が少ない終盤など安定した結果が必要な局面では、平均だけでなくばらつきも判断材料にすべき

🚀 次のステップ

  • 発展: 今日はP_HIT=0.20(探索の当たり確率)を固定したが、コンペの種類によってはもっと低い場合もある。P_HITを0.05〜0.30の範囲で変化させたとき、常に探索する戦略と適応的ルールの優劣がどう変わるか試してみましょう。探索が外れた場合に何らかのペナルティ(余分な提出消費・作業時間の圧迫)を加えたシミュレーションへの拡張も可能です
  • 次回予告: Phase6テーマ4「コンペ戦略・Gold 3枚達成プラン」の3日目に入ります。次回はチーム戦(チームマージ)の意思決定、1人で最後まで戦うか途中で他の参加者とチームを組むかの判断を、スコアの伸びと労力配分の観点から扱う予定です

Phase 6 の学習マップ(全20テーマ)

1-5
SOTA手法追跡・論文実装(完了)
6-10
カスタムアーキテクチャ設計(完了)
11-15
高度なアンサンブル(完了)
16-20
コンペ戦略・Gold3枚達成プラン(本日2/5)

Day141で最終提出の選び方(CV vs Public LB)、Day142で進行中の探索/活用トレードオフを実測。次回以降、チームマージ判断や提出予算の実務判断に進む。

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

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