Day 179 — 2026-10-12 | Playground Series 実戦投入⑭ — 作り直しの1本(lr0.05・600本)の本数を選ぶ。5本平均の山は葉127 までは600本、葉255 は約450本、葉511 は約225本。補正CV で本数を選ぶと葉255/511 で −0.86/−3.66 得するが、決定15 が低雑音で選ぶ葉63/127 では得0。fold ごとの早期打ち切り(忍耐50)は対で +2.17 悪化(2/2)し、生CV は 2〜8 楽観になる(×10⁻⁴、低雑音2ワールドだけの暫定値)

2026-10-12 金 / Phase 6(月替わりテーマ2: Playground Series 実戦投入) 分析→戦略 低雑音2ワールド(予定18、tmp 消失で中断)・50 fit・1ワールド約1,130秒

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

今の決め方。 決定15(lr0.1・300本の葉 31/63/127/255 を補正CVで比べ、端の255が勝ったら511を足す)で葉を選び、決定18 でその葉を学習率 0.05・600本で作り直して提出する。Day178 の1 fold モデルの曲線では、葉255 以上で 600本は多すぎた(中雑音 s255 は450本付近、s511 は300本付近が山)。ただし提出は 5本平均 で、平均で過学習が和らぐ分だけ山が後ろにずれるかもしれない。これは測っていなかった。

比べる止め方。 Kaggle の公開ノートブックで最も多いのは、fold ごとに検証 fold の誤差を見て early_stopping(stopping_rounds=50〜100) で止める書き方。LightGBM 公式も early_stopping コールバックを「検証スコアが stopping_rounds 回改善しなければ止める」と説明している(lightgbm.early_stopping、参照 2026-10-12)。この書き方は 止める本数を選んだのと同じ fold で OOF を測る ので、CV が楽観的になりうる。

用語整理:

用語意味
s31〜s511学習率 0.05・最大600本・葉 31〜511 の乱数なし LightGBM(Day178 と同じ)
途中本数 n50, 100, …, 600 の12点。学習し直さず、木の塊ごとの予測を足し上げて作る
T55本平均の truth 60,000行 RMSLE(真)
Ca補正CV √(C² − A_test)(決定13)。C は生の OOF RMSLE、A_test は test 2万行での5本の分散
早期打ち切り(忍耐50 / ∞)fold ごとに val fold の l2 が最良の本数で止める。忍耐50 は「50本改善が無ければ打ち切り」、∞ は600本までの最良
損規則で選んだ本数の T5 − その葉の12点の T5 の最良

数値はすべて縮小シミュレータの上の実測(S5E5 の構造を写した合成データ、Day163〜)。

データ・保存物の構造(スキーマ)

名前形・型意味・作り方使う場所
Sex … Body_Temp, Dur_x_HR, Keytel_kcalint / float(9列)Day163〜178 と同じ合成(ワールドの乱数 3000+番号)。今日測れたのは低雑音(Calories の雑音 0.05/0.03)の w0・w1全候補
raw179_w*.npz(今日、消失)oof (5,12,30000) float32・oof_es (2,5,30000)・S (5,12,3)・S_es (2,5,3)・best_it (2,5,5)・vcurve (5,5,600)葉5種 × 途中本数12点の OOF と、5本平均の T5・T1・A_test。早期打ち切り2通り。val の l2 を1本ごと表1〜6
raw178(Day178、消失)oof (6,30000)・evk (6,5,80000)600本の予測の一致確認(fold 別予測 最大差 0.0)再現
raw175 / raw176(lr0.1、消失)oof・evk決定15 の葉選び(補正CV)に使う表5
T5 / C / A / Cafloat5本平均の truth RMSLE(真)/生の OOF RMSLE/test 側5本の分散/補正CV √(C²−A)全表

評価指標(RMSLE)と、今日の問い

RMSLE は log1p 空間の RMSE(小さいほど良い)。差はすべて ×10⁻⁴。下の図は、提出と同じ5本平均で、本数を変えたときの真の誤差を葉ごとに重ねたもの。600本より良い本数があるのは葉255・511 だけだった。

5本平均の真の RMSLE − 同じ葉の600本(×10⁻⁴、下ほど良い、低雑音2ワールド平均)0 より下 = 600本より良い本数がある。葉255・511 だけが 0 を下回る-5+0+5+10+15+20100本200本300本400本500本600本葉31葉63葉127葉255葉511

❓ 問題

タスクA(予想を書いてから測る)

  1. 5本平均の山(最良の本数)は、葉ごとにどこにあると思うか。1 fold の曲線(Day178 表6)より前か後ろか
  2. 補正CV で本数を選ぶと、600本固定よりどれくらい得か
  3. fold ごとの早期打ち切りは、補正CV で本数を選ぶのと比べて良いか悪いか。生CV はどう見えるか
  4. 葉と本数を同時に60点から選ぶと、決定18+本数選びより良いか

タスクB(コーディング)

  1. day178.py の生成部を変えずに、s31〜s511 を学習しながら val fold の l2 を1本ごとに記録し、途中本数12点の OOF と fold 別予測を作る day179.py を書く(全体を12回予測せず、start_iteration で塊ごとに足す)
  2. 600本の予測が Day178 の保存物と一致することを確かめる
  3. analyze179.py で、5本平均の本数曲線・本数を選ぶ規則の損・CV が本数の曲線を正しく写すか・早期打ち切りの止まる本数と楽観・方針全体の損・費用を出す

タスクC(分析・戦略)

  1. 決定18 の「600本」を、葉ごとの本数選びに替えるべきか
  2. fold ごとの早期打ち切りを最終提出に使ってよいか
  3. 今日の結果で、どの決定の印を動かせるか

💡 ヒント

ヒント1(方向性)

本数を選ぶ得は「選んだ葉」によって大きく違うはず。葉ごとの表と、決定15 を通した方針全体の表を分けて見る。

ヒント2(アプローチ)

LightGBM の回帰では、predict(X, start_iteration=a, num_iteration=b-a, raw_score=True) が a〜b 本目の木の寄与だけを返す。塊を足し上げれば12点の予測が1回分の手間で取れる。早期打ち切りは record_evaluation で val の l2 を1本ごとに取っておけば、学習後に「忍耐50ならどこで止まったか」を再現できる(止め方の違いでモデルを作り直す必要がない)。

ヒント3(コード骨格)

rec = {}
m = lgb.LGBMRegressor(**LG, num_leaves=L, random_state=k).fit(
    X, y, eval_set=[(XV, yv)], eval_metric="l2", callbacks=[lgb.record_evaluation(rec)])
sv, se_ = np.zeros(len(va)), np.zeros(len(XE)); a = 0
for i, b in enumerate(IT):
    sv += m.predict(XV, start_iteration=a, num_iteration=b - a, raw_score=True)
    se_ += m.predict(XE, start_iteration=a, num_iteration=b - a, raw_score=True)
    oof[j, i, va] = sv; ev[j, i, k] = se_; a = b

✅ 模範解答

タスクA(予想。測る前に書いたもの、04:35 記入)

  1. 5本平均の山は1 fold の曲線より後ろ。中雑音 s255 は500本前後、s511 は350〜400本、低雑音 s255/s511 は200〜300本。葉31/63 は600本が最良
  2. 大きい葉で −0.5〜−1.5 の得、小さい葉では差なし
  3. 早期打ち切り(忍耐50)は生CV が +0.5 以上楽観になるが、真の損は補正CV 最小とほぼ同じ
  4. 候補が増えた分だけ損が増え、決定18+本数選びより悪い

→ 1 は低雑音の s511(約225本)は当たり、s255 は 450本で外れ(後ろ)。2 は葉511 で −3.66 と予想より大きい。3 は外れ: 真の損も +2.17 悪化した。4 は2ワールドでは逆(−0.35)だが差は1SE。

タスクB(完全なコード)

day179.py(生成部 keytel_rate・make_original・synth_from・add_feats・BASE は day178.py と1文字も同じなので省略):

LG = dict(objective="regression", learning_rate=0.05, n_estimators=600, min_child_samples=20, n_jobs=1, verbose=-1)
LEAVES = [31, 63, 127, 255, 511]
names = [f"s{L}" for L in LEAVES]
IT = list(range(50, 601, 50))
R8 = np.load(sys.argv[4])                                   # Day178 の raw178_w<W>.npz
assert list(R8["names"]) == ["lgb_det"] + names            # 決定9
# ...生成(seed 3000+W)、train / evalset を作り、R8 の ytr・yev と一致を assert...
oof = np.zeros((nM, nI, N_TRAIN), np.float32)              # 途中本数ごとの OOF(保存)
ev = np.zeros((nM, nI, 5, N_TEST + N_TRUTH), np.float32)   # 途中本数ごとの fold 別予測(保存せず集計だけ残す)
vcurve = np.zeros((nM, 5, 600))                            # fold ごとの val l2(1本ごと)
ES = {"es_inf": None, "es_p50": 50}

def es_pick(c, pat):                                       # 忍耐 pat で止めたときの最良本数
    best, bi = np.inf, 0
    for i, v in enumerate(c):
        if v < best: best, bi = v, i
        elif pat is not None and i - bi >= pat: break
    return bi + 1

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
    yv = np.log1p(train.Calories.values[va])
    for j, L in enumerate(LEAVES):
        rec = {}
        m = lgb.LGBMRegressor(**LG, num_leaves=L, random_state=k).fit(
            X, y, eval_set=[(XV, yv)], eval_metric="l2", callbacks=[lgb.record_evaluation(rec)])
        vcurve[j, k] = rec["valid_0"]["l2"]
        sv, se_ = np.zeros(len(va)), np.zeros(len(XE)); a = 0
        for i, b in enumerate(IT):
            sv += m.predict(XV, start_iteration=a, num_iteration=b - a, raw_score=True)
            se_ += m.predict(XE, start_iteration=a, num_iteration=b - a, raw_score=True)
            oof[j, i, va] = sv; ev[j, i, k] = se_; a = b
        full = m.predict(XE)
        rep[j, 2] = max(rep[j, 2], np.abs(full - se_).max())                                   # 塊の和 対 一括
        rep[j, 0] = max(rep[j, 0], np.abs(sv - R8["oof"][j + 1, va]).max())                    # 対 raw178 OOF
        rep[j, 1] = max(rep[j, 1], np.abs(full.astype(np.float32) - R8["evk"][j + 1, k]).max())  # 対 raw178 fold別
        for e, pat in enumerate(ES.values()):
            bi = es_pick(vcurve[j, k], pat); best_it[e, j, k] = bi
            oof_es[e, j, va] = m.predict(XV, num_iteration=bi); ev_es[e, j, k] = m.predict(XE, num_iteration=bi)

def agg(E):  # (5, N_TEST+N_TRUTH) -> T5, T1, A_test
    t5 = np.sqrt(((lp_clip(E[:, N_TEST:].mean(0)) - ltru) ** 2).mean())
    t1 = np.sqrt(((lp_clip(E[:, N_TEST:]) - ltru) ** 2).mean())
    return t5, t1, E[:, :N_TEST].var(axis=0).mean()
S = np.array([[agg(ev[j, i]) for i in range(nI)] for j in range(nM)])
S_es = np.array([[agg(ev_es[e, j]) for j in range(nM)] for e in range(len(ES))])
np.savez_compressed(f"raw179_w{W}.npz", oof=oof, oof_es=oof_es, S=S, S_es=S_es, best_it=best_it, ...)

analyze179.py(要点):

def choose(g, col, lo=50):                 # 本数を CV(Ca か C)の最小で選ぶ
    gg = g[g.it >= lo]; return gg.loc[gg[col].idxmin()]

# 決定15 の葉(lr0.1・300本の補正CV、一覧 31/63/127/255+端なら511)
L15 = fca[[31, 63, 127, 255]].idxmin()
if L15 == 255: L15 = fca[[31, 63, 127, 255, 511]].idxmin()
P = {"決定18(葉は決定15、600本)": gl[gl.it == 600].T5.iloc[0],
     "決定18+本数を補正CVで(50-600)": choose(gl, "Ca").T5,
     "決定18+本数を補正CVで(200-600)": choose(gl, "Ca", 200).T5,
     "決定18+早期打ち切り(忍耐50)": el.loc["es_p50"].T5,
     "決定18+本数を生CVで": choose(gl, "C").T5,
     "葉も本数も補正CVで(60点)": g.loc[g.Ca.idxmin()].T5,
     "葉は600本の補正CVで(lr0.05の5本)": g600.loc[g600.Ca.idxmin()].T5}
loss = (P[k] - g.T5.min()) * 1e4          # 対 その世界の 5葉×12本=60点の真の最良

実行:

# 18ワールド(3世界 × w0-5)を 6 並行で作る予定で起動した(run.sh は Day177 の uv venv と scikit-learn 同梱の libomp を使う)
xargs -P 6 -L 1 ./run.sh < jobs.txt
# 集計(引数: 今日の出力・Day175 の保存物・Day176 の保存物・Day177 で足した Day176 の残り)
python analyze179.py r <d175> <d176> <d177/r176>

実行結果(実測。低雑音 w0・w1 の2ワールドだけ)

# ============ 実行環境: macOS (M1, メモリ8GB)・Day177 の uv venv(Python 3.13.12・lightgbm 4.7.0)
#   OMP_NUM_THREADS=1・n_jobs=1、6並行。1ワールド 1,127〜1,130 秒(低雑音)
#   18ワールドで起動したが、低雑音 w0・w1 の2つが終わった後(約1時間後)に /private/tmp が丸ごと片付けられ、
#   実行中のジョブ・今日の保存物・Day175〜178 の保存物と venv がすべて消えた(理由は「扱わなかった範囲」)。
#   下の表は、消える前に analyze179.py を2ワールドで回した出力。
ワールド数: {'低雑音': 2}
再現(600本 対 raw178、最大絶対差): OOF 2.3e-14  fold別予測 0.0  塊の和 対 一括予測 3.0e-14

=== 表1 5本平均の真の RMSLE の本数曲線(同じ葉の600本との差、×1e-4、2ワールド平均)===
[低雑音]  100     200     300     400     500    600   最良本数(w0,w1)  600本が最良
  葉31  +23.24  +11.05  +6.44  +3.41  +1.34  0.00   600, 600            2/2
  葉63  +19.93   +8.70  +4.64  +2.31  +0.87  0.00   600, 600            2/2
  葉127 +16.72   +5.95  +2.95  +1.34  +0.52  0.00   600, 600            2/2
  葉255 +12.19   +1.85  -0.13  -0.79  -0.72  0.00   400, 500            0/2
  葉511  +3.86   -4.80  -4.32  -2.93  -1.44  0.00   200, 250            0/2

=== 表2 葉ごとに本数を選ぶ規則(損 / 600本固定との対の差±SE、×1e-4)===
                       葉31          葉63          葉127         葉255          葉511
  600本固定            0.00/ 0       0.00/ 0       0.00/ 0       0.86/ 0        4.94/ 0
  補正CV最小(50-600)   0.00/+0.00    0.00/+0.00    0.00/+0.00    0.00/-0.86±0.25 1.28/-3.66±0.48
  補正CV最小(200-600)  (50-600 と同じ)
  生CV最小             0.36/+0.36    0.10/+0.10    1.06/+1.06    1.42/+0.56±1.37 0.14/-4.80±0.35
  早期打ち切り(忍耐∞)  1.02/+1.02    0.54/+0.54    1.02/+1.02    1.18/+0.32±0.80 0.33/-4.61±0.16
  早期打ち切り(忍耐50) 1.83/+1.83    2.58/+2.58    1.93/+1.93    2.59/+1.74±0.62 0.71/-4.22±0.23

=== 表3 CV は本数の曲線を正しく写すか((CV[200]−CV[600]) − (真[200]−真[600])、×1e-4)===
  補正CV Ca  葉31 +2.38  葉63 +4.14  葉127 +5.27  葉255 +2.55  葉511 +3.04   (本数の少ない側を悪く見せる)
  生CV  C    葉31 -2.68  葉63 -2.24  葉127 -2.46  葉255 -6.34  葉511 -6.77   (本数の少ない側を良く見せる)

=== 表4 早期打ち切りが止まった本数(fold ごと10個の中央値[最小-最大])と生CV の楽観 ===
  忍耐50  葉31 506[243-590]  葉63 350[242-600]  葉127 364[263-438]  葉255 199[159-281]  葉511 172[131-234]
  忍耐∞   葉31 572[408-600]  葉63 570[312-600]  葉127 444[263-593]  葉255 280[197-353]  葉511 180[139-234]
  忍耐50 の生CV の楽観の増分((C_es−真_es) − (C600−真600)): 葉31 -2.41  葉63 -2.23  葉127 -3.10  葉255 -7.16  葉511 -8.28

=== 表5 方針全体の損(×1e-4、対 その世界の 5葉×12本=60点の真の最良)と決定18 との対の差 ===
  決定18(葉は決定15、600本)         1.46
  決定18+本数を補正CVで(50-600)      1.46   +0.00(決定15 は w0=葉63・w1=葉127 を選び、どちらも600本が最良)
  決定18+本数を補正CVで(200-600)     1.46   +0.00
  決定18+本数を生CVで                1.78   +0.32±0.32(悪い1/2)
  決定18+早期打ち切り(忍耐50)        3.63   +2.17±0.28(悪い2/2)
  葉も本数も補正CVで(60点)            1.11   -0.35±0.35(良い1/2)
  葉は600本の補正CVで(lr0.05の5本)    1.11   -0.35±0.35(良い1/2)

=== 表6 費用(1ワールド5fold、中央値秒)===
  学習(val の記録込み): 葉31 6  葉63 8  葉127 13  葉255 21  葉511 42
  予測(途中本数12点+早期打ち切り2通り): 葉31 46  葉63 67  葉127 197  葉255 330  葉511 397

タスクC(分析・戦略の模範)

  1. 決定18 の「600本」は、低雑音では替えても何も変わらない。 決定15 が低雑音で選ぶ葉は 63/127 で、その葉の5本平均は 2/2 で 600本が最良(表1)。補正CV で本数を選んでも 600本を選び、損は 1.46 のまま(表5)。本数選びが効くのは葉255 以上だけで、葉255 は −0.86±0.25、葉511 は −3.66±0.48(表2)。決定15 が葉255 を選ぶ小得・中雑音で同じ得が出るかが本題だが、今日は測れなかった。決定18 は変えず、本数選びは「葉255 以上を選んだときだけ補正CV で 200〜600本から選ぶ」を仮の決定19(印 △)として置く
  2. fold ごとの早期打ち切り(忍耐50)は最終提出に使わない。 対で +2.17±0.28(2/2 悪化)。止まる本数が早すぎ(葉63 で中央値 350本、真の山は600本)、しかも生CV は止めた fold の上で測るので 2〜8 楽観に見える(表4)。早期打ち切りの CV で候補を比べると、葉が大きい候補ほど得に見える(楽観が 葉31 −2.41 → 葉511 −8.28 と育つ)ので、比べる段階にも使えない。忍耐∞ でも小さい葉で +0.5〜+1.0 悪い。原因は、1 fold の val 6,000行の l2 の揺れで本数を決めることと、5本平均では山が後ろにずれること(1 fold の山 ≠ 5本平均の山)
  3. 補正CV は本数の比較では偏る。 (CV[200]−CV[600]) − (真の差) は 補正CV で +2.4〜+5.3(少ない本数を悪く見せる)、生CV で −2.2〜−6.8(良く見せる)。補正CV の偏りは「600本へ寄せる」向きなので、葉63/127 のように真の山が600本にあるときは害がない。葉511 では偏りがあっても真の山(約225本)の近くを選べた(損 1.28)。決定13(補正CV)は「葉の比較」で ◎ だが、「本数の比較」では偏りが残ることを CV設計書に書き足す
  4. 印を動かせる決定は無い。 2ワールド・1世界だけでは、決定18(○)を ◎ にも ✕ にもできない。暫定値として残し、次回やり直す

タスクD(決定)

#決定採った案採らなかった案と理由前提が変わったら
18(印 ○ のまま)最終提出は決定15 の葉を lr0.05・600本で作り直した1本低雑音2ワールドでは、本数を選んでも 600本と同じ(損 1.46)—小得・中雑音(決定15 が葉255 を選ぶ世界)で本数選びが −0.5 以上の得を出したら、決定19 と合わせて「600本」を「葉255 以上は補正CV で選ぶ」に替える
19(新設、印 △)作り直しの葉が255 以上のときだけ、本数を 200〜600 本から補正CV 最小で選ぶ。それ未満は600本葉255 −0.86±0.25・葉511 −3.66±0.48(2/2)。小さい葉では補正CV が600本を選ぶので、規則を全部の葉にかけても害は無い(a) fold ごとの早期打ち切り(忍耐50): 方針で +2.17(2/2 悪化)、生CV が 2〜8 楽観。(b) 生CV で本数を選ぶ: 少ない本数を良く見せる偏り(−2〜−7)で葉127 で +1.06。(c) 60点から葉と本数を同時に選ぶ: −0.35±0.35 で差なし、費用は5本を全部 lr0.05 で作る分(Day178 で2.6倍)2ワールド・低雑音だけなので △。小得・中雑音で葉255 の得が出なければ取り下げる
早期打ち切りの扱い(決定にはしない、メモ)比べる段階でも提出でも fold ごとの早期打ち切りは使わない楽観が葉とともに育つ(−2.4 → −8.3)ので、大きい葉を選ばせる向きに偏る忍耐を100以上にする: 忍耐∞ でも小さい葉で +0.5〜+1.0 悪いので、忍耐の長さだけでは直らないval fold が大きい(行数10倍)コンペでは val の揺れが小さくなり、結論が変わりうる

📊 結果の可視化

低雑音2ワールドだけの暫定値(18ワールドの予定が tmp の片付けで中断)。印は動かしていない。
方針全体の損(×10⁻⁴、対 60点の真の最良、低雑音2ワールド平均、短いほど良い)右の注記は決定18 との対の差。決定15 は葉63/127 を選んだので本数選びは効かなかった葉も本数も補正CVで(60点)1.11(±0.35 差なし)決定18(600本)1.46(基準)決定18+本数を補正CVで1.46(+0.00)決定18+本数を生CVで1.78(+0.32±0.32)決定18+早期打ち切り(忍耐50)3.63(+2.17±0.28 2/2悪化)
早期打ち切り(忍耐50)の生CV の楽観(×10⁻⁴、左へ長いほど CV が良く見えすぎ)と止まった本数葉31-2.41 止まった本数 中央値 506葉63-2.23 止まった本数 中央値 350葉127-3.10 止まった本数 中央値 364葉255-7.16 止まった本数 中央値 199葉511-8.28 止まった本数 中央値 172

1枚目: 決定15 が低雑音で選ぶ葉63/127 では600本が最良なので、本数選びは損を変えない。早期打ち切りは +2.17 悪化。2枚目: 早期打ち切りの CV は葉が大きいほど良く見えすぎる。

🪜 Step-by-Step 解説

Step 1: 予想を先に書く

「5本平均の山は1 fold の山より後ろ」「早期打ち切りは CV が楽観になるが真の損は同じ」と書いてから測った。後者は外れ、真の損も悪かった。

Step 2: 学習し直さずに途中本数を作る

木の塊ごとの寄与を足し上げる(start_iteration)と、一括予測との差は最大 3.0e-14、600本の時点で Day178 の保存物と fold 別予測 0.0・OOF 2.3e-14 で一致した(決定9)。OOF の 2.3e-14 は足し算の順序が違うだけの丸め誤差。

Step 3: 5本平均の山を見る

提出の形(5本平均)で12点の真の誤差を出した。葉127 までは 600本でもまだ下がっている。葉255 は 400〜500本、葉511 は 200〜250本が山。

Step 4: 葉ごとの規則と、方針全体を分けて見る

葉ごとの表(表2)だけ見ると「補正CV で本数を選ぶと −3.66 得」と言いたくなるが、決定15 は低雑音で葉511 を選ばない。方針全体(表5)では得0。どの葉で効くかと、その葉が実際に選ばれるかは別の問い。

Step 5: 早期打ち切りがなぜ負けたかを分ける

止まった本数(表4)と、CV の楽観(表4 最下行)を別々に出した。「早く止まりすぎる」と「止めた fold で測るので良く見える」の2つが重なっている。

Step 6: データが消えたときの書き方

2ワールドしか無いので、決定の印は動かさず、△ の決定19 だけ置いた。予定と実際の差(18 → 2ワールド)と原因を「扱わなかった範囲」に書いた。

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

なぜ5本平均の山は1本の山より後ろにずれるのか。 本数を増やすと、1本のモデルは偶然まで覚え始める(過学習)。5本の fold モデルは学習データの8割ずつが違うので、覚えた偶然も少しずつ違う。平均するとその偶然の部分が打ち消し合うので、5本平均では「まだ過学習の害が小さい」ところが長く続く。だから山が後ろにずれる。Day178 の1 fold の曲線で「多すぎ」に見えた本数が、5本平均では最良(葉63/127 の600本)だったのはこのため。

早期打ち切りの楽観は「選んだ人が採点する」問題。 val fold で一番良かった本数を選び、同じ val fold で点数をつけると、偶然良かった分まで点数に入る。テストで答えを見てから「この問題だけ採点して」と言うのと同じ。葉が大きいほど本数ごとの点数の揺れが大きく、偶然の上振れも大きくなる(−2.4 → −8.3)。

補正CV が本数比較で偏る理由(仮説)。 補正CV は「5本の予測のばらつき(A_test)」を引く。本数が少ないほどモデルは似通い、A_test が小さくなるので引く量も小さい。一方で、少ない本数の OOF と truth の差の構造は多い本数と違うので、引き算が釣り合わない。今日はこの仮説を3項分解(Day177)で確かめていない。

🏆 Kaggleでの実践的な使い方

  • fold ごとの early stopping の CV を、そのまま比較や提出の判断に使わない。 今日のデータでは CV が 2〜8×10⁻⁴ 楽観で、その楽観は大きい木ほど育った。比べるなら本数を固定するか、止める本数を CV の外(別の分割)で決める
  • 本数は「提出と同じ形(fold 平均)」で選ぶ。 1 fold の曲線より山が後ろに来る。葉が小さい(〜127)なら、学習率 0.05 の600本は短すぎるくらい
  • 本数の調整が効くのは大きい木だけ。 葉255 以上で 600本固定は損(低雑音で 0.86〜4.94)。自分の選ぶ葉がそこに入るかを先に確かめる
  • 実験の保存物は /tmp に置かない。 今日は OS 側の片付けで、1時間の計算と過去4日分の保存物が消えた

⚠️ よくある誤解・ミス

誤解・ミスなぜ起こるか正しい理解
early stopping を使えば本数の調整は済む公開ノートブックの定番で、CV も良く見える低雑音2ワールドで方針の損が +2.17 悪化。CV は 2〜8 楽観
1 fold の学習曲線で本数を決める1本の曲線が一番手軽提出は5本平均で、山は後ろにずれる(葉63: 1本では 450本付近、5本平均では600本)
補正CV は何を比べても偏らない葉の比較で ◎ だった本数の比較では少ない本数を悪く見せる(+2.4〜+5.3)。向きが「多い本数」寄りなので害が出にくいだけ
葉ごとの表で得が出れば方針でも得表2 だけ見る決定15 がその葉を選ばなければ得は0(表5)
2ワールドの結果で決定を更新する数字が出ると使いたくなる2ワールド・1世界では印を動かさない。△ の仮決定に留める

🧭 扱わなかった範囲と理由

  • 小得・中雑音と、低雑音 w2-5(予定18 → 実際2ワールド): 6並行で起動し、低雑音 w0・w1 が終わった後(起動から約1時間後、2026-10-12 05時台)に /private/tmp が丸ごと片付けられた(直前はディスク空き 3.1GB・使用率99%、片付け後は空き 36GB)。今日のジョブ・スクリプト・raw179 に加えて、Day175〜178 の保存物(raw175/176/178)と Day177 の uv venv も消えた。決定15 の葉選びに要る lr0.1 の保存物が無いため、残りは今日のうちに回し直せなかった。本題(決定15 が葉255 を選ぶ世界で本数選びが効くか)は未計測
  • 作り直しの手順: 次回は保存物を /tmp ではなく ~/AI/lab/学習/Engineer/DataScience/Kaggle/work/(今日作成。HTML 生成の work/d179/mk_html179.py を置いた)に置き、lr0.1 の葉31〜511(決定15 用)と lr0.05 の途中本数を1本のスクリプトで作る。1ワールド約20〜40分 × 18 で、Mac mini なら半日以内の見込み(M1 での実測 1,130秒/ワールドから導出、中雑音は未実測)
  • 補正CV の本数方向の偏りの分解(Day177 の3項): 未計測
  • 学習率 0.02 以下、seed を変えた独立な10分割、mcs 5・10、乱数ありの候補(決定14)、候補数 K と選択の上振れ、系統別の m、公開LB と2本目の枠、avg3、OOF側とtest側の seed の揺れ、noKeytel・raw7、入れ子CV との比較、Day170 の出し直し、Day165 の SE 個数差、Day162 の選択バイアス、真の効果0の偽採用率: 持ち越し
  • 原典照合: LightGBM の early_stopping API の説明は今日照合した。Nadeau & Bengio・Krogh & Vedelsby は未照合のまま
  • 実データ(S5E5): 縮小シミュレータの上の数字のみ

🚀 次のステップ

  • 発展: 早期打ち切りの止める本数を「別の分割の OOF」で決めると、楽観と早すぎる停止の両方が消えるか
  • 発展: 補正CV の本数方向の偏り(+2.4〜+5.3)を Day177 の3項に分けて、どの項が作っているか
  • 次回予告: Day 180 — Playground Series 実戦投入⑮(今日消えた分のやり直し: 保存先を tmp の外に移し、lr0.1 の葉31〜511 と lr0.05 の途中本数12点を1本のスクリプトで、小得・中雑音から先に回す。決定19 を小得・中雑音で確かめ、決定18 の「600本」を替えるか決める)。月替わりテーマ3「画像(CV)系コンペへの応用」は 10/25 から

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

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