📚 背景知識(読んでから問題へ)
Day175 のおさらい。 乱数なしの葉 31(lgb_det)・63・127 を3種類の世界で比べた。
| 世界(雑音 元/合成) | leaf63 の真の差 | leaf127 の真の差 |
|---|---|---|
| 低雑音(0.05/0.03) | −3.67 | −3.50(引き分け) |
| 小得(0.12/0.08) | −12.72 | −22.96 |
| 中雑音(0.15/0.10) | −16.47 | −31.74 |
(×10⁻⁴、lgb_det との差。マイナスが良い)
小得・中雑音では葉 127 がまだ良くなり続けていた。表の端(一番大きい葉)が最良なら、その先にもっと良いものがあるかもしれない。 これが今日の問いの出発点。
木の大きさを決める2つのつまみ(LightGBM):
| つまみ | 意味 | 大きくすると |
|---|---|---|
num_leaves | 1本の木の葉の最大数(木の細かさ) | 細かい組み合わせを拾える。ただし雑音も拾いやすく、fold ごとの予測もばらつく |
min_child_samples | 1枚の葉に最低いる行数(既定 20) | 葉が「少人数の偶然」を覚えにくくなる(正則化)。ただし細かい規則も拾えなくなる |
LightGBM の公式ドキュメント(Parameters Tuning、参照 2026-10-09・記憶による要約で未照合)は、num_leaves を上げると精度が上がりうるが過学習の危険があり、min_data_in_leaf(= min_child_samples)は過学習を防ぐ重要なつまみ、と説明している。どちらに転ぶかはデータ次第なので、今日は測る。
用語整理(Day175 と同じものは省略):
| 用語 | 意味 |
|---|---|
| DET8 | 乱数なしの8本: lgb_det(葉31)・leaf63・leaf127・leaf255・leaf511・leaf127_m50・leaf255_m50・leaf511_m50(_m50 は min_child_samples=50) |
| 候補一覧 L3/L4/L5/L8 | 決定15 で比べる一覧。L3={31,63,127}(Day175)、L4=L3+255、L5=L4+511、L8=DET8 全部 |
| 一覧内 oracle | その一覧の中で真に最良のものを選べたときの損。一覧が狭いことによる損(取りこぼし) |
| A比 | test 行の fold 間分散 A_test の、lgb_det に対する倍率。生の CV の偏りの大きさの目安(Day171) |
数値はすべて縮小シミュレータの上の実測。実データ(S5E5)で同じになるとは限らない。
データ・保存物の構造(スキーマ)
| 名前 | 形・型 | 意味・作り方 | 使う場所 |
|---|---|---|---|
| Sex … Body_Temp, Dur_x_HR, Keytel_kcal | int / float(9列) | Day163〜175 と同じ合成(ワールドの乱数 3000+番号)。Calories の雑音だけ 0.05/0.03(低)・0.12/0.08(小得)・0.15/0.10(中) | 全候補 |
| raw176_w*.npz(low/mid/sm) | oof (6,30000)・evk (6,5,80000) float32・fit_s (6,) | 今日作った乱数なし6本(葉127/255/511 × mcs 20/50)。9 ワールド × 6本 × 5 fold = 270 fit | 表0〜5 |
| raw175(葉31/63/127) | oof (3,30000)・evk (3,5,80000) | Day175 の保存物。今日の leaf127 と最大差 0.0 で一致を確認 | 再現テスト・一覧 L3 |
| raw172 / raw173 / raw174 | oof (11〜13, seed, 30000)・evk (…, 5, 80000) | 乱数ありの候補(seed 組 s0・s1) | 表5(決定14) |
| C / Ca / T5 / A | float | 生の CV(1モデルの OOF)/補正CV √(C²−A)/真(6万行 truth で5 fold 平均を採点)/test 行の fold 間分散 | 表1〜5 |
評価指標(RMSLE)と、生の CV の偏り
RMSLE は log1p 空間の RMSE(小さいほど良い)。生の CV は1モデルの OOF で採点するが、提出は5モデルの平均なので、1本ごとの予測がばらつく候補(葉が多い木)ほど不利に見える。下の図は、中雑音で葉127/255/511 を生の CV(赤)がどれだけ悪く見せたかと、補正CV(緑)で残った偏り。
❓ 問題
タスクA(予想)— 測る前に書く
- 中雑音で、葉 255・511 は葉 127 よりさらに良いか。山(最良点)はどこにあるか
min_child_samplesを 20→50 にすると、葉の多い木は良くなるか(「葉を増やした分の過学習を抑える」と期待できるか)- 生の CV の偏り(大きい木を悪く見せる量)は葉 511 でどこまで育つか。補正CV(決定13)はそれでも消せるか
タスクB(コーディング)
day175.pyの生成部(ワールドの乱数 3000+番号・雑音だけ引数)をそのまま使い、DET を6本に差し替えたday176.pyを書く。leaf127(mcs20)が raw175 のleaf127と OOF・fold 別 test 予測の両方で完全一致することを確かめる(決定9)。候補ごとの学習秒も保存する- 低雑音・小得・中雑音を回す(今日は途中で打ち切った。打ち切った事実と世界ごとの件数を結果に書く)
- 集計
analyze176.pyで、(a) 真の差と隣どうしの差、(b) 生と補正の CV の偏り、(c) 候補一覧 L3〜L8 ごとの基準選びの損、(d) 乱数ありの候補まで含めた方針の損、を出す
タスクC(分析と戦略)
- 山はどこにあり、世界でどう動くか。
min_child_samples=50の結果をどう読むか - 候補一覧はどこまで広げるべきか。費用(学習秒)と損の両方で答える
- 決定13・15 の印をどう付け替えるか。打ち切った9ワールドで言えること・言えないことを分ける
💡 ヒント
ヒント1(方向性)
「一覧の端が選ばれたら、その先を足す」。一覧の真ん中が選ばれているなら、もう広げる必要は無い。
ヒント2(アプローチ)
一覧を広げた損の変化は、一覧内 oracle(取りこぼし)と選び間違い(補正CVの揺れ)に分けて見る。一覧を広げると取りこぼしは減るが、候補が増えると選び間違いと選択の上振れが増えうる。
ヒント3(コード骨格)
LISTS = {"L3": ["lgb_det", "leaf63", "leaf127"], "L4": [..., "leaf255"], "L5": [..., "leaf511"], "L8": DET8}
for (kind, w), h in S0[S0.cand.isin(DET8)].groupby(["kind", "world"]):
h = h.set_index("cand"); best = h.T5.min()
for ln, L in LISTS.items():
p = h.Ca[L].idxmin() # 補正CVで選ぶ(決定15)
o = h.T5[L].idxmin() # 一覧内 oracle
rows.append(dict(L=ln, oracle=(h.T5[o]-best)*1e4, regret=(h.T5[p]-best)*1e4))
✅ 模範解答
タスクA(予想の模範と、実測との照合)
| 予想 | 模範の予想(書いた時点) | 実測(9ワールド) | 当たり外れ |
|---|---|---|---|
| 1. 山の位置 | 中雑音は 255 か 511 まで良くなる | 中雑音: 255 が最良 −36.62±0.63(5/5)、511 は 255 より +7.69±0.75 悪い。小得も 255 が最良(1ワールドのみ)。低雑音は 63/127 が山で 255 は lgb_det より +2.48 悪い(0/3) | 半分当たり。山は 255 で、511 は過ぎていた。山の位置は雑音で動く(低雑音 63〜127、小得・中雑音 255) |
| 2. mcs50 | 葉の多い木では良くなる | 6組すべてで悪化: 葉127 +9.80〜+12.88、葉255 +11.05〜+19.25、葉511 +8.68〜+24.72 | 外れ。この合成データでは細かい規則を拾う方が効き、少人数の葉を禁じると損をする |
| 3. 生の偏り | 葉511 で +25 くらい | 中雑音 leaf511 +42.29±3.01(A比 3.98)。補正後 +3.23±3.06(約1SE) | 大きさは外れ(もっと大きい)。補正CVは中雑音ではほぼ消した。ただし低雑音 leaf511 は補正後 −4.78±1.76(3ワールド、約2.7SE)で、逆向きに少し効きすぎの兆し |
タスクB(完全なコード)
day176.py(生成部は day175.py と1文字も変えていない。差し替えたのは DET の一覧・保存名・学習秒の記録だけ):
# --- day175.py からの差分だけ(生成器 keytel_rate / make_original / synth_from / add_feats は同一)--- LG = dict(objective="regression", learning_rate=0.1, n_estimators=300, num_leaves=31, min_child_samples=20, n_jobs=1, verbose=-1) # Day176: 木の大きさの上限を測る。leaf127 は raw175 との一致確認を兼ねる DET = [("leaf127", dict(num_leaves=127)), ("leaf255", dict(num_leaves=255)), ("leaf511", dict(num_leaves=511)), ("leaf127_m50", dict(num_leaves=127, min_child_samples=50)), ("leaf255_m50", dict(num_leaves=255, min_child_samples=50)), ("leaf511_m50", dict(num_leaves=511, min_child_samples=50))] names = [c[0] for c in DET] rng = np.random.default_rng(3000 + W) orig = make_original(N_ORIG, rng) pool = synth_from(orig, N_TRAIN + N_TEST + N_TRUTH, rng) orig, pool = add_feats(orig), add_feats(pool) train = pool.iloc[:N_TRAIN].reset_index(drop=True) evalset = pool.iloc[N_TRAIN:].reset_index(drop=True) XE = evalset[BASE].values oof = np.zeros((len(names), N_TRAIN)) evk = np.zeros((len(names), 5, N_TEST + N_TRUTH), np.float32) fit_s = np.zeros(len(names)) # 候補ごとの学習+予測の秒数(費用の表に使う) for k, (tr, va) in enumerate(KFold(5, shuffle=True, random_state=0).split(train)): tr_df = pd.concat([train.iloc[tr], orig], ignore_index=True) X, y, XV = tr_df[BASE].values, np.log1p(tr_df.Calories.values), train.iloc[va][BASE].values for j, (nm, over) in enumerate(DET): t1 = time.time() m = lgb.LGBMRegressor(**{**LG, **over}, random_state=1000 * 0 + k).fit(X, y) oof[j, va] = m.predict(XV); evk[j, k] = m.predict(XE) fit_s[j] += time.time() - t1 print(f"world {W} fold {k} {time.time()-T0:.0f}s", flush=True) assert names == ["leaf127", "leaf255", "leaf511", "leaf127_m50", "leaf255_m50", "leaf511_m50"] # 決定9 np.savez_compressed(f"raw176_w{W}.npz", oof=oof, evk=evk, ytr=train.Calories.values, yev=evalset.Calories.values, names=np.array(names), fit_s=fit_s) print(f"world {W} wall {time.time()-T0:.0f}s")
analyze176.py(要点。保存物 raw172/173/174 の乱数あり10本+raw175 の葉31/63/127+今日の6本を同じワールドで突き合わせる):
def stats(y, ytru, O, E): # O: OOF (30000,) E: fold 別 test 予測 (5, 80000) c = sle(y, O).mean(); a = E[:, :N].var(axis=0).mean(); t5 = sle(ytru, E.mean(0)[N:]).mean() return dict(C=np.sqrt(c), Ca=np.sqrt(c - a), T5=np.sqrt(t5), A=a) # 生CV・補正CV(決定13)・真・A_test # 決定9: ラベルの一致と leaf127 の完全一致を assert/記録 assert np.array_equal(a6["ytr"], ytr) and np.array_equal(a6["yev"], yev) rep.append(dict(l127_oof=np.abs(a6["oof"][0] - a5["oof"][2]).max(), l127_ev=np.abs(a6["evk"][0].astype(np.float64) - a5["evk"][2]).max())) LISTS = {"L3 {31,63,127}": ["lgb_det", "leaf63", "leaf127"], "L4 +255": [..., "leaf255"], "L5 +511": [..., "leaf511"], "L8 +mcs50": DET8} for (kind, w), h in S0[S0.cand.isin(DET8)].groupby(["kind", "world"]): h = h.set_index("cand"); best = h.T5.min() for ln, L in LISTS.items(): p = h.Ca[L].idxmin(); o = h.T5[L].idxmin(); pr = h.C[L].idxmin() rows.append(dict(kind=kind, world=w, L=ln, oracle=(h.T5[o] - best) * 1e4, regret=(h.T5[p] - best) * 1e4, raw=(h.T5[pr] - best) * 1e4, pick=p)) # 表5: 決定15(L3 または L8 で基準)→ 決定14(乱数ありの補正CV最良が m 以上良ければ乗り換え)を全19本の最良と比べる base = h.Ca[LISTS[ln]].idxmin(); b = h.Ca[RAND].idxmin() p = b if (h.Ca[base] - h.Ca[b]) * 1e4 > m else base
実行:
# 1ワールド分を作る(run.sh は Day175 と同じく DYLD_LIBRARY_PATH に scikit-learn 同梱の libomp を入れて day176.py を呼ぶ) ./run.sh 0 0.12 0.08 $(pwd)/sm # 残りを 5 並行で回す(今日は 9 ワールドで打ち切り) (for w in 0 1 2 3 4 5; do echo "$w 0.05 0.03 low"; echo "$w 0.15 0.10 mid"; done; for w in 1 2 3 4 5; do echo "$w 0.12 0.08 sm"; done) | xargs -P 5 -L 1 sh -c './run.sh $0 $1 $2 '"$(pwd)"'/$3' # 保存物(d172・d173/mid・d174/sm・d175)と今日の raw176 を合わせて集計する python analyze176.py <d172> <d173/mid> <d174/sm> <d175>
実行結果(実測・途中打ち切り)
# ============ 実行環境: macOS (M1, メモリ8GB)・Day172 と同じ uv venv(Python 3.13.12・lightgbm 4.7.0)
# OMP_NUM_THREADS=1・n_jobs=1。他のセッション・VM と同居し load average 20〜70
# 完了: 低雑音 3(w0-2)・小得 1(w0)・中雑音 5(w0-4)= 9 ワールド × 6 本 × 5 fold = 270 fit
# 1ワールドの壁時計 339〜997 秒。予定 40 ワールドのうち 31 は未計測(打ち切り)
=== 表0 再現(決定9): 今日の leaf127 と raw175 の leaf127 の最大絶対差 ===
中雑音 0.0 0.0 / 低雑音 0.0 0.0 / 小得 0.0 0.0
=== 表0b 費用: 1ワールド5fold の学習+予測秒(中央値、同居環境の実測)===
leaf127 30 / leaf255 60 / leaf511 139 / leaf127_m50 43 / leaf255_m50 85 / leaf511_m50 175
=== 表1 真の差 T5−T5(lgb_det)(×1e-4、ワールド平均±SE)と lgb_det より良い割合 ===
[低雑音] leaf63 -3.18±0.77(1.00) leaf127 -2.27±0.71(1.00) leaf255 +2.48±0.67(0.00) leaf511 +10.30±0.80(0.00)
leaf127_m50 +7.53±1.14(0.00) leaf255_m50 +13.54±0.73(0.00) leaf511_m50 +18.98±1.09(0.00)
[小得] leaf63 -12.41 leaf127 -23.77 leaf255 -26.54 leaf511 -18.88 leaf127_m50 -10.97 leaf255_m50 -9.04 leaf511_m50 +1.58(1ワールド、SE なし)
[中雑音] leaf63 -15.56±0.71(1.00) leaf127 -31.42±0.35(1.00) leaf255 -36.62±0.63(1.00) leaf511 -28.93±1.36(1.00)
leaf127_m50 -18.54±0.78(1.00) leaf255_m50 -17.37±0.96(1.00) leaf511_m50 -4.21±1.64(0.80)
=== 表1b 隣どうしの真の差(×1e-4)===
[低雑音] 255−127 +4.75±0.72 511−255 +7.82±0.14 127_m50−127 +9.80±1.62 255_m50−255 +11.05±1.40 511_m50−511 +8.68±1.72
[小得] 255−127 -2.78 511−255 +7.67 127_m50−127 +12.80 255_m50−255 +17.50 511_m50−511 +20.46
[中雑音] 255−127 -5.20±0.45 511−255 +7.69±0.75 127_m50−127 +12.88±0.73 255_m50−255 +19.25±0.42 511_m50−511 +24.72±0.68
=== 表2 CV の偏り(CV差−真の差、対 lgb_det、×1e-4)と A比 ===
[低雑音] leaf127 生 +9.34±2.28 補正 -2.95±2.17 A比 1.97 | leaf255 生 +17.28±1.02 補正 -2.73±1.06 A比 2.60 | leaf511 生 +22.04±1.70 補正 -4.78±1.76 A比 3.16
[小得] leaf127 生 +7.32 補正 -7.18 A比 2.14 | leaf255 生 +12.02 補正 -12.55 A比 2.94 | leaf511 生 +23.84 補正 -10.68 A比 3.75(1ワールド)
[中雑音] leaf127 生 +15.27±3.37 補正 -1.43±3.41 A比 2.25 | leaf255 生 +28.92±3.94 補正 +0.94±4.06 A比 3.11 | leaf511 生 +42.29±3.01 補正 +3.23±3.06 A比 3.98
=== 表3 候補一覧を広げたときの基準選び(対 DET8 の真の最良、×1e-4)===
[低雑音] L3: oracle 0.00 補正CV選択 1.31±0.80 生CV選択 3.01 | L4/L5/L8: 同じ(255 以上は1回も選ばれない)
[小得] L3: oracle 2.78 選択 2.78 | L4/L5/L8: oracle 0.00 選択 0.00(leaf255 を選択)
[中雑音] L3: oracle 5.20 選択 5.20±0.45 生CV 5.20 | L4/L5/L8: oracle 0.00 選択 0.74±0.74(leaf255 4・leaf127 1)生CV 5.20
DET8 の真の最良: 低雑音 {leaf63: 2, leaf127: 1} 小得 {leaf255: 1} 中雑音 {leaf255: 5}
ワールド対 L8 − L3: 低雑音 +0.00 小得 -2.78 中雑音 -4.46±1.14(良くなった 4/5、悪くなった 0)
=== 表5 全19本(乱数あり10+乱数なし8)の真の最良との損(s0/s1 平均)===
低雑音 小得 中雑音 最悪の超過 乗り換え回数
決定15 L8 → 決定14 m=0/2 1.31 0.00 0.74 0.74 0
常にleaf255 6.05 0.00 0.00 4.75 0
決定15 L3 → 決定14 m=0/2 1.31 2.78 5.20 5.20 0
常にleaf127 1.31 2.78 5.20 5.20 0
常にleaf255_m50 17.11 17.50 19.25 19.25 0
タスクC(分析の模範)
- 木の大きさには山があり、山は雑音で動く。 中雑音では 127→255 で −5.20±0.45 良くなり、255→511 で +7.69±0.75 戻る(5/5 で同じ向き)。低雑音では山がもっと手前(63〜127)で、255 はもう
lgb_detより悪い(+2.48、0/3)。min_child_samples=50は6組すべてで悪化(最小 +8.68)。この合成データの目的変数は Keytel 式の掛け算と少しの雑音で、細かい組み合わせを拾うほど得をする。葉を増やすことと「少人数の葉を禁じる」ことは同じ「木の大きさ」ではなく、ここでは前者が効き後者が害になった - 一覧は L4(31/63/127/255)まで広げれば今日の世界では足りる。 L3→L4 で中雑音の損 5.20→0.74(4/5 で改善、悪化 0)、小得 2.78→0。511 と mcs50 を足した L5・L8 は損を1つも変えなかった(どの世界でも選ばれない)。費用(1ワールド5fold、同居環境の実測)は葉255 が 60 秒(葉127 の約2倍)、葉511 が 139 秒、mcs50 の3本で計 303 秒。L3→L4 で足すのは 60 秒、L4→L8 でさらに 442 秒を足して得が 0。止め方は「一覧の端(一番大きい葉)が補正CVで最良になったら、次の倍(例: 511)を1本だけ足す。真ん中が最良なら止める」とする。今日の9ワールドでは端の255 が選ばれた中雑音・小得で 511 を足しても選ばれなかったので、このルールは「1本足して止まる」
- 印の更新。 決定15 は ○ のまま一覧を L4 に改訂し、上の止め方を追記。決定13(補正CV)は中雑音で葉511 の生の偏り +42.29 を +3.23±3.06 まで消したので効いているが、低雑音の葉511 で −4.78±1.76(3ワールド)、小得で −4〜−12(1ワールド)と、大きい木で補正が効きすぎる兆しがある。ワールド数が足りず SE が出ない世界もあるので、◎ は「葉 127 まで」に限り、葉 255 以上は ○ に下げる(後で測り直す)
タスクD(決定)
| # | 決定 | 採った案 | 採らなかった案と理由 | 前提が変わったら |
|---|---|---|---|---|
| 15(改訂、印 ○ 据え置き) | 基準の候補一覧 | 乱数なしの 葉 31/63/127/255(L4)を補正CVで比べる。一覧の端が最良になったら次の倍を1本だけ足し、真ん中が最良なら止める | (a) L8(511・mcs50 も入れる): 損は L4 と同じで、1ワールドあたり 442 秒余分にかかる。(b) 常に leaf255: 中雑音・小得では 0 だが低雑音で 6.05(最悪の超過 4.75)。(c) L3 のまま: 中雑音で 5.20 の取りこぼし | 学習率・木の本数を変えると山の位置も動く(今日は lr 0.1・300本で固定)。実データで葉255 が端なら 511 を足す |
| 15b(新設、印 △) | min_child_samples は既定の 20 から上げない | mcs50 は6組すべてで +8.7〜+24.7 悪化 | (a) mcs50 を一覧に入れ続ける(得 0・費用 303 秒)。(b) mcs を下げる(5・10)方向は未計測 | 雑音が大きく行数が少ないデータ(実データで行数が1桁少ない場合)では逆になりうる。そこでは mcs 10/20/50 を測り直す |
| 13(補正CV、印を分割) | 葉127 まで ◎、葉255 以上 ○ | 中雑音の葉511 で生 +42.29→補正 +3.23(約1SE) | ◎ のまま(低雑音 −4.78±1.76 の兆しを無視することになる) | 残り31ワールドを回して、葉255 以上の補正の残りが 2SE を超え続けるなら、A_test の推定誤差(fold 5個で分散を取る)を疑う |
| 14(休眠のまま) | 乗り換え m=2 | 決定15(L4/L8)のあとも乗り換え 0 回(m=0/2 で損が同じ) | — | 乱数ありの候補を葉 255 の上で作り直したら測り直す |
📊 結果の可視化
1枚目: 3世界での最悪の超過損。一覧に葉255 を足した L4 は 0.74 で、Day175 の L3(5.20)から中雑音の取りこぼしが消えた。L8(511・mcs50 も入れる)は L4 と同じ。2枚目: 葉の数ごとの真の差。中雑音・小得は葉255 が山で、葉511 は戻る。3枚目: min_child_samples=50 は6組すべてで悪化。いずれも9ワールドの途中集計(残り31ワールドは未計測)。
🪜 Step-by-Step 解説
Step 1: 作るものを最小にする
乱数ありの候補(Day172〜174)と葉31/63/127(Day175)は保存してある。今日作るのは新しい6本だけで、そのうち leaf127 は再現確認のための重複。
Step 2: 先に再現を確かめる
今日の leaf127 と Day175 の leaf127 を OOF・fold 別 test 予測で比べ、3世界とも最大差 0.0。ラベルも np.array_equal で assert。ここが崩れていたら、保存物と今日の予測を混ぜた表は全部意味を失う(決定9)。
Step 3: 予定を変えたことを記録する
1ワールドが同居環境で 6〜17 分かかり、40 ワールドは数時間になると分かった。9ワールドで打ち切り、どの世界が何ワールドかを結果の先頭に書いた(低3・小得1・中5)。小得は1ワールドなので SE が出ず、結論の根拠には使わない(方向の確認だけ)。
Step 4: 真の差を「隣どうし」で見る
lgb_det との差だけを見ると「葉511 も −28.93 で良い」と読めてしまう。隣どうし(255→511)の差 +7.69±0.75 を見て初めて、山を越えたことが分かる。
Step 5: 一覧を広げた損を「取りこぼし」と「選び間違い」に分ける
L3→L4 で中雑音の一覧内 oracle は 5.20→0(取りこぼしが消えた)、選び間違いは 0.74(1ワールドで leaf127 を選んだ)。L5・L8 は oracle も選択も変わらない。広げても悪くならなかったのは、足した候補(511・mcs50)が真に大きく悪く、補正CVでも選ばれなかったため。
Step 6: 費用を並べて止め方を決める
得の出ない候補に時間を払わない。「端が最良なら1本足す」は、今日の世界では L4 で止まり、511 は確認の1回だけで済む。
📐 数学・統計の補足(文系向け)
「山がある」とは。 写真の解像度に例えると、葉の数は画素数。画素が少なすぎると輪郭がぼやけ(拾えない規則がある)、多すぎると撮影時のノイズまで細かく写る。ちょうど良い画素数はノイズの量で決まる。低雑音ではノイズが少なく、もとから少ない画素で足りる…と思いきや、今日の結果では低雑音の山が手前(63〜127)、中雑音の山が奥(255)だった。これは「雑音が大きいほど、正解の形を細かく描かないと平均で損をする」ことを示し、直感と逆向き。理由として考えられるのは、雑音を足したことで目的変数のばらつきが増え、5 fold 平均(提出)が1本ごとの細かすぎる部分を打ち消してくれるから。この説明は仮説で、今日は分離していない。
生の偏りが葉とともに増える理由。 Day171 の恒等式: 生の CV² − 補正CV² = A_test(5本の予測のずれの大きさ)。葉が多い木ほど fold ごとの予測がずれる(A比 葉127 約2倍・葉511 約4倍)ので、1本だけで採点する生の CV は大きい木を不利に見せる。中雑音の葉511 で +42.29 は、真の差(−28.93)より大きい。生の CV だけを見ると「葉511 は葉31 より悪い」と真逆の判断になる。
🏆 Kaggleでの実践的な使い方
num_leavesは倍々(31→63→127→255)で並べ、一覧の端が勝ったら次の倍を1本足す。 真ん中が勝ったら止める。全部の格子を回すより速く、今日は得を1つも落とさなかったnum_leavesとmin_child_samplesを同じ「複雑さ」として扱わない。 葉を増やして mcs で抑える、という組み合わせは今日のデータでは両方の悪いところを取った(葉511_m50 は葉511 より +8.7〜+24.7 悪い)- 大きい木を比べるときこそ生の CV を使わない。 提出が fold 平均なら、生の CV は葉511 を最大 +42 悪く見せる。test 予測の fold 間分散を候補ごとに引く(決定13)
- 時間の上限を先に決め、打ち切ったら件数を書く。 今日は 40→9 ワールド。どの数字が何ワールドからかを表の上に書いておけば、後から足しても混ざらない
⚠️ よくある誤解・ミス
| 誤解・ミス | なぜ起こるか | 正しい理解 |
|---|---|---|
| 葉511 は葉31 より良いので大きいほど良い | 基準との差だけを見る | 隣どうしで見ると 255→511 は +7.69 悪化。山を越えている |
min_child_samples を上げれば大きい木の過学習が直る | 「正則化は大きいモデルの味方」という一般論 | 今日のデータでは6組すべて悪化。細かい規則を拾えなくなる害の方が大きい |
| 候補を増やすほど選択の上振れで損をする | 候補数 K の害だけを考える | 今日は足した候補が真に大きく悪く、補正CVでも選ばれなかったので損は増えなかった(L4→L8 で差 0)。害が出るのは、真に近い候補を多数足したとき |
| 9ワールドの結果を40ワールドと同じ重さで扱う | 打ち切りを書かない | 小得は1ワールド(SE なし)。結論は中雑音5・低雑音3の方向のそろい方(5/5、3/3)だけに頼る |
| 補正CV はどんな大きさの木でも偏りを消す | 中雑音で効いたことを一般化 | 低雑音の葉511 で −4.78±1.76(効きすぎの兆し)。葉255 以上は印を ○ に下げて測り直す |
🧭 扱わなかった範囲と理由
- 残り31ワールド(未計測): 低雑音 w3〜15(13)、小得 w1〜11(11)、中雑音 w5〜11(7)。他の処理と同居した 8GB 機で1ワールド 6〜17 分かかり、全部回すと数時間かかるため打ち切った。とくに小得は1ワールドしかなく、表の小得の数字は SE が無い
- 学習率・木の本数との組み合わせ: lr 0.1・300本で固定。山の位置は学習率を下げて本数を増やすと動くはず(未計測)
min_child_samplesを下げる方向(5・10): 上げると悪いことは分かったが、下げると良いかは未計測- 補正CVの効きすぎの原因: A_test を fold 5個の分散で推定する誤差が、大きい木で増えているのではないかという仮説。未分離
- 乱数ありの候補を葉255 で作り直すこと(決定14 の再検証): 持ち越し
- 実データ(S5E5): 縮小シミュレータの上の数字のみ。S5E5 のルール確認・公開割合・敵対的AUC は Day171 で利用者の作業として切り出したまま
- 持ち越し: 候補数 K と選択の上振れ、系統別の m(
et)、公開LB と2本目の枠、avg3 での損、OOF 側と test 側の seed の揺れの分離、noKeytel・raw7の補正後のずれ、補正CV と入れ子CV の比較、Day170 のハイブリッド・狭い10本の出し直し、SE 個数差と分割の新しさ(Day165)、Day162 の選択バイアス、真の効果ちょうど0の偽採用率、Nadeau & Bengio・Krogh & Vedelsby 原典、LightGBM 公式ドキュメントの原文照合
🚀 次のステップ
- 発展: 残り31ワールドを、他の処理が少ない時間(または Mac mini 48GB)で回し、表1〜5 を40ワールドで出し直す。小得の SE と、補正CV の葉255 以上での効きすぎ(低雑音 −4.78)が残るかを確かめる
- 発展: 学習率 0.05・600本で葉 63/127/255 を作り、山の位置が動くかを見る
- 次回予告: Day 177 — Playground Series 実戦投入⑫(補正CV の効きすぎの原因: 大きい木で A_test の推定誤差が増えるかを、fold 数 5/10 と test 行数で分けて測る。あわせて未計測ワールドの追加分で表を更新)。月替わりテーマ3「画像(CV)系コンペへの応用」は 10/25 から
📝 自己評価(解いた後に記入)
自分の回答・気づき・メモ: