📚 背景知識(読んでから問題へ)
ここまでの決め方(今日の前の CV設計書の該当部分)。
| 決定 | 中身 | 印(今日の前) |
|---|---|---|
| 13 | 候補は補正CV √(C² − A_test) で比べる。C は生の OOF RMSLE、A_test は test 2万行での5本の予測の分散(ラベル不要) | 葉511 まで ◎、本数比較(200 対 600)○ |
| 15 | lr0.1・300本の乱数なし LightGBM 葉 31/63/127/255 を補正CV で比べ、端の255 が最良なら 511 を1本足す | ◎ |
| 18 | 決定15 で選んだ葉を lr0.05・600本で作り直した1本を最終提出にする | ◎ |
| 19 | 葉511 のときだけ本数を 200〜600 から補正CV で選ぶ(休眠) | 撤回(休眠の一部のみ) |
Day180 が残した穴。 葉31〜127 の5本平均の山は「600本」だった。しかし600本は測った範囲の右端なので、「600本が最良」は「600本より先を測っていない」とも読める。決定18 の「600本」は、本数の山ではなく測った上限だった。
もう1つの穴: 早期打ち切り(early stopping)。 Kaggle で一番よく使われる本数の決め方は「fold ごとに validation の誤差が 50本続けて下がらなければ止める」(LightGBM の early_stopping(stopping_rounds=50))。Day180 は止まる本数だけ数え(山より 40〜320本早い)、その止め方で作った提出の真の誤差は測っていなかった。
今日の作り方。 Day180 と1文字も同じ生成器(ワールドの乱数 3000+番号、40ワールド=低雑音16・小得12・中雑音12)で、lr0.05・1,200本の乱数なし LightGBM 葉 31/63/127/255 を5 fold 作る。途中本数 50〜1,200(24点)の OOF と5本平均を start_iteration の塊の和で作り、さらに fold ごとの早期打ち切り(忍耐50・忍耐∞=val 最小)で止めた本数の予測も同じモデルから取る。葉511 は Day180 で山が 200〜450本(40/40 で600本より前)だったので入れない。決定15 の葉は Day180 の lr0.1 の保存物(work/d180/d180.pkl)から読む。
用語整理:
| 用語 | 意味 |
|---|---|
s31〜s255 | 学習率 0.05・最大1,200本の乱数なし LightGBM(決定18 の作り直し) |
| 途中本数 n | 50, 100, …, 1,200 の24点。学習し直さず塊ごとに予測を足す |
| T5 | 5本平均の truth 60,000行 RMSLE(真) |
| 損 | 方針で選んだ1本の T5 − その世界の lr0.05 格子(葉31〜255×24点+葉511×12点)の T5 の最良 |
| 忍耐50 / 忍耐∞ | fold ごとに val l2 を1本ずつ見て、50本続けて改善しなければ止める/上限までの val 最小で止める。止めた本数は fold ごとに違う |
| 対の差 | 同じワールドで2つの方針の T5 の差を取り、ワールドについて平均±SE |
数値はすべて縮小シミュレータの上の実測(S5E5 の構造を写した合成データ、Day163〜)。実データ(Kaggle Playground S5E5)では測っていない。
データ・保存物の構造(スキーマ)
| 名前 | 形・型 | 意味・作り方 | 使う場所 |
|---|---|---|---|
| Sex … Body_Temp, Dur_x_HR, Keytel_kcal | int / float(9列) | Day163〜180 と同じ合成(ワールドの乱数 3000+番号)。低雑音16・小得12・中雑音12 | 全候補 |
| oof5 | (4, 24, 30000) float32 | lr0.05 の葉31〜255 × 途中本数24点(50〜1,200)の OOF | 生CV・補正CV |
| S5 | (4, 24, 4) | T5²・T1²・A_test・A_truth | 表1〜3 |
| oofes / SE / nes | (4, 2, 30000) / (4, 2, 4) / (4, 2, 5) int | 早期打ち切り(忍耐50・∞)で止めた OOF・集計・fold ごとの止めた本数 | 表2・表4 |
| vcurve | (4, 5, 1200) | fold ごとの val l2 を1本ごと | 早期打ち切りの再現 |
| d180.pkl(読むだけ) | L15・G・G1 | Day180 の決定15 の葉と葉511 の12点 | 方針・格子の最良 |
| 保存先 | work/d181/r/{low,sm,mid}/raw181_w*.npz(計 413MB) | tmp の外 | 次回以降 |
評価指標(RMSLE)と、今日の問い
RMSLE は log1p 空間の RMSE(小さいほど良い)。差はすべて ×10⁻⁴。下の図は、提出と同じ5本平均で本数を 1,200本まで伸ばしたときの真の誤差。600本(Day180 の上限)の右に山がある葉と、600本付近で山を越える葉がある。方針が選ぶ葉で山が違うので、固定の本数では3世界を同時に満たせない。
❓ 問題
タスクA(予想を先に書く)
測る前に、次の3つに「当たる/外れる」と理由を書く。
- 葉31〜127 の5本平均の山は600本より後ろにある。葉255 は600本付近
- 1,200本に固定すれば、600本固定(決定18)より3世界とも良くなる
- fold ごとの早期打ち切り(忍耐50、上限1,200)は、600本固定より悪い。補正CV ならその悪さを見抜ける
タスクB(コーディング)
day180.pyの生成部を1文字も変えずに、lr0.05・1,200本の葉31〜255 を作り、24点の途中本数と、忍耐50・忍耐∞の早期打ち切りの予測を1回の学習から取るday181.pyを書く- 600本までの12点が Day180 の保存物と一致することを確かめる(再現)
analyze181.pyで、本数曲線(〜1,200本)・方針全体の損・本数方向の CV の偏り(1200 対 600、900 対 600、300 対 600)・早期打ち切りの本数と真の損・費用を出す
タスクC(分析と決定)
- 決定18 の「600本」を何に替えるか(固定本数を延ばす/本数を CV で選ぶ/早期打ち切り)。候補ごとに対の差と最悪の損を並べて決める
- 補正CV(決定13)は 600 対 1,200本の比較にも使えるか。早期打ち切りの候補の評価にも使えるか
- CV設計書(決定1〜20)を1枚に書き直す。印(◎○△✕)と「前提が変わったら」を付ける
💡 ヒント
ヒント1(方向性)
「葉ごとの山」と「方針が実際に選ぶ葉の山」を分けて見る(Day180 と同じ)。小得・中雑音では決定15 が葉255 を選ぶので、葉31 の山がどこにあっても方針には効かない。
ヒント2(アプローチ)
早期打ち切りは、学習し直さなくても再現できる。record_evaluation で val l2 を1本ごとに記録しておけば、「50本続けて改善しない最初の点」を後から数えられる。止めた本数で predict(num_iteration=n) すれば、早期打ち切りで作ったのと同じ予測になる(乱数なしなので、本数 n までの木は上限1,200 のモデルと同じ)。
ヒント3(コード骨格)
def stop_at(v, pat): # val l2 の記録 v から、忍耐 pat で止めたときの best_iteration(1始まり) b, best = 0, np.inf for i, x in enumerate(v): if x < best: best, b = x, i elif pat is not None and i - b >= pat: break return b + 1 for e, pat in enumerate([50, None]): n = stop_at(vcurve[j, k], pat) oofes[j, e, va] = m.predict(XV, num_iteration=n); eves[j, e, k] = m.predict(XE, num_iteration=n)
✅ 模範解答
タスクA(判定)
- 前半は当たり、後半は世界で分かれる。 葉31・63 は小得・中雑音で 12/12 が1,200本で最良(まだ下がり続けている)。葉127 も 600本の後ろ(低雑音 11/16、小得・中雑音 12/12)。葉255 は低雑音で 400本(0/16 が600本より後ろ)、小得 650本(7/12)、中雑音 700本(11/12)で、600本の少し後ろ
- 外れ。 1,200本固定は低雑音で −1.08±0.40 と良いが、小得 +1.93±1.17・中雑音 +3.03±0.75 と悪くなる。決定15 が小得・中雑音で選ぶ葉255 の山は 650〜700本で、1,200本では +3.9〜+4.0 の覚えすぎ。固定本数はどれも3世界を同時に満たさない(800本固定も小得 −0.28±0.49・中雑音 −0.16±0.31 で 2SE 未満)
- 前半は当たり(大きく悪い)、後半は外れ。 忍耐50 は 40ワールドすべてで決定18 より悪い(+4.91±0.33、z +14.7)。忍耐∞(val 最小)も 35/40 で悪い(+2.05±0.29)。しかし補正CV で比べても、早期打ち切りは 600本より 1.5〜2.7 良く見えすぎる(対の偏り、全12組で負)。補正CV でも早期打ち切りの悪さは見抜けない
タスクB(完全なコード)
day181.py(生成部 keytel_rate・make_original・synth_from・add_feats・BASE は Day180 と1文字も同じなので省略。全文は work/d181/day181.py):
NMAX = 1200 LG5 = dict(objective="regression", learning_rate=0.05, n_estimators=NMAX, min_child_samples=20, n_jobs=1, verbose=-1) LEAVES = [31, 63, 127, 255] # 葉511 は Day180 で山が 200〜450本(40/40)なので入れない IT = list(range(50, NMAX + 1, 50)) # 24点 nM, nI = len(LEAVES), len(IT) # ...生成(seed 3000+W、雑音 NO/NS)、train 3万行・evalset 8万行(先頭2万 = test、残り6万 = truth)... def lp_clip(lp): # 提出は 1kcal 以上に丸める(Day163〜) return np.log1p(np.clip(np.expm1(lp), 1, None)) def stop_at(v, pat): # val l2 の記録から、忍耐 pat の早期打ち切りが返す best_iteration(1始まり) b, best = 0, np.inf for i, x in enumerate(v): if x < best: best, b = x, i elif pat is not None and i - b >= pat: break return b + 1 oof5 = np.zeros((nM, nI, N_TRAIN), np.float32) ev5 = np.zeros((nM, nI, 5, N_TEST + N_TRUTH), np.float32) # 集計だけ残す(保存しない) ES = [50, None] # 忍耐50 と 忍耐∞ oofes = np.zeros((nM, len(ES), N_TRAIN)); eves = np.zeros((nM, len(ES), 5, N_TEST + N_TRUTH)) nes = np.zeros((nM, len(ES), 5), int); vcurve = np.zeros((nM, 5, NMAX)) 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(**LG5, 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) oof5[j, i, va] = sv; ev5[j, i, k] = se_; a = b for e, pat in enumerate(ES): # 早期打ち切りを後から再現 n = stop_at(vcurve[j, k], pat); nes[j, e, k] = n oofes[j, e, va] = m.predict(XV, num_iteration=n); eves[j, e, k] = m.predict(XE, num_iteration=n) def agg(E): # (5, N_TEST+N_TRUTH) -> T5², T1², A_test, A_truth t5 = ((lp_clip(E[:, N_TEST:].mean(0)) - ltru) ** 2).mean() t1 = ((lp_clip(E[:, N_TEST:]) - ltru) ** 2).mean() return t5, t1, E[:, :N_TEST].var(axis=0).mean(), E[:, N_TEST:].var(axis=0).mean() S5 = np.array([[agg(ev5[j, i].astype(np.float64)) for i in range(nI)] for j in range(nM)]) SE = np.array([[agg(eves[j, e]) for e in range(len(ES))] for j in range(nM)]) np.savez_compressed(f"{OUT}/raw181_w{W}.npz", oof5=oof5, S5=S5, oofes=oofes.astype(np.float32), SE=SE, nes=nes, vcurve=vcurve, ...)
run.sh と実行:
# run.sh: 1ワールドを1スレッドで作る。venv は Day180 のもの(work/d180/.venv)を使い、sklearn 同梱の libomp を渡す V=../d180/.venv export OMP_NUM_THREADS=1 DYLD_LIBRARY_PATH="$V/lib/python3.13/site-packages/sklearn/.dylibs" $V/bin/python day181.py "$1" "$2" "$3" "r/$4" > "log/$4_w$1.log" 2>&1 # 40ワールドを14並行(小得 → 中雑音 → 低雑音の順。決定15 が葉255 を選ぶ世界を先に) xargs -P 14 -L 1 ./run.sh < jobs_o.txt # 集計(Day180 の d180.pkl から決定15 の葉と葉511 の12点を読む) ../d180/.venv/bin/python analyze181.py r ../d180 > a40.txt ../d180/.venv/bin/python extra181.py > extra181.txt
analyze181.py の方針部分(全文は work/d181/analyze181.py):
def choose(gl, col, lo=50, hi=1200): # 本数を CV(Ca か C)の最小で選ぶ gg = gl[(gl.it >= lo) & (gl.it <= hi)]; return gg.loc[gg[col].idxmin()] L = L15[(kn, w)]; gl = g[g.leaf == L] # 決定15 の葉(Day180 の lr0.1 から) POL[(kn, w)] = { "決定18(lr0.05・600本)": gl[gl.it == 600].T5.iloc[0], "800本固定": gl[gl.it == 800].T5.iloc[0], "1200本固定": gl[gl.it == 1200].T5.iloc[0], "本数を補正CVで(600-1200)": choose(gl, "Ca", 600).T5, "本数を生CVで(50-1200)": choose(gl, "C").T5, "fold ごとの早期打ち切り 忍耐50(上限1200)": e.loc["忍耐50", "T5"], "葉も本数も補正CVで(4葉×24点)": g.loc[g.Ca.idxmin()].T5, "best": min(g.T5.min(), b511)} # 葉511 は Day180 の12点の最良
実行結果(実測)
# ============ 実行環境: macOS・Apple M5 Pro(15コア・48GB)・uv venv(Python 3.13.16・lightgbm 4.7.0・scikit-learn 1.9.1・
# numpy 2.5.4・pandas 3.0.6、Day180 と同じ venv)。OMP_NUM_THREADS=1・n_jobs=1、14並行
# 40ワールド(低雑音16・小得12・中雑音12)× 葉4種 × 5 fold = 800 fit(各1,200本)
# 壁時計 14分41秒(04:12:59→04:27:40)、1ワールド 211〜362秒。保存物 r/{low,sm,mid}/raw181_w*.npz 計 413MB(work/d181)
ワールド数: {'中雑音': 12, '低雑音': 16, '小得': 12}
塊の和 対 一括予測(fold 0、最大絶対差): 4.4e-14
=== 表0 再現: 50〜600本の12点(葉31〜255)を Day180 の raw180 と比べる(最大絶対差、40ワールド)===
OOF(float32): 0.0e+00(完全一致) S5(T5²・T1²・A_test・A_truth): 1.2e-09(float32 で持った途中和の丸めの差)
=== 表1 5本平均の真の RMSLE(同じ葉の600本との差、×1e-4、平均±SE)と最良本数 ===
[低雑音 n=16] 300 500 700 900 1200 最良本数 中央値[範囲] 600より後ろ 1200が最良
葉31 +6.16±0.21 +1.39±0.06 -1.14±0.05 -2.67±0.09 -3.94±0.19 1200[1150-1200] 16/16 14/16
葉63 +4.83±0.23 +0.97±0.06 -0.62±0.07 -1.30±0.15 -1.34±0.23 1075[900-1200] 16/16 4/16
葉127 +2.70±0.21 +0.31±0.06 -0.01±0.04 +0.41±0.10 +1.76±0.18 725[500-850] 11/16 0/16
葉255 -0.61±0.19 -0.63±0.05 +0.77±0.06 +2.45±0.08 +4.97±0.12 400[300-500] 0/16 0/16
[小得 n=12]
葉31 +11.62±0.31 +3.14±0.08 -2.59±0.07 -6.67±0.16 -10.96±0.24 1200[1200-1200] 12/12 12/12
葉63 +12.50±0.31 +3.06±0.06 -2.43±0.09 -6.29±0.19 -9.78±0.28 1200[1200-1200] 12/12 12/12
葉127 +11.14±0.29 +2.52±0.10 -1.64±0.05 -3.46±0.11 -4.22±0.18 1175[1050-1200] 12/12 6/12
葉255 +5.43±0.26 +0.48±0.07 +0.08±0.08 +1.30±0.14 +4.00±0.21 650[550-750] 7/12 0/12
[中雑音 n=12]
葉31 +14.22±0.28 +3.80±0.10 -3.28±0.08 -8.55±0.20 -14.42±0.31 1200[1200-1200] 12/12 12/12
葉63 +16.03±0.35 +4.10±0.12 -3.30±0.09 -8.29±0.19 -13.32±0.30 1200[1200-1200] 12/12 12/12
葉127 +14.94±0.41 +3.36±0.12 -2.42±0.12 -5.09±0.20 -6.49±0.31 1200[1150-1200] 12/12 7/12
葉255 +8.07±0.28 +0.96±0.13 -0.24±0.08 +0.88±0.19 +3.86±0.29 700[600-800] 11/12 0/12
=== 表2 方針全体の損(×1e-4、対 その世界の lr0.05 格子の真の最良)と決定18 との対の差(平均±SE、良くなった/悪くなったワールド数)===
方針 低雑音 小得 中雑音 最悪 40ワールドまとめ(z)
決定15 のまま(lr0.1・300本) 2.65 (+0.56±0.23) 4/12 2.65 (+0.46±0.23) 6/6 2.54 (+1.19±0.21) 0/12 2.65 +0.72±0.14 (+5.2)
決定18(lr0.05・600本) 2.09 2.19 1.36 2.19 —
800本固定 1.15 (-0.94±0.17) 14/2 1.91 (-0.28±0.49) 3/9 1.19 (-0.16±0.31) 4/8 1.91 -0.51±0.19 (-2.7)
1000本固定 0.83 (-1.26±0.29) 13/3 2.78 (+0.60±0.87) 3/9 2.48 (+1.12±0.56) 1/11 2.78 +0.01±0.36 (+0.0)
1200本固定 1.01 (-1.08±0.40) 12/4 4.12 (+1.93±1.17) 3/9 4.38 (+3.03±0.75) 1/11 4.38 +1.05±0.52 (+2.0)
本数を補正CVで(600-1200) 0.78 (-1.31±0.26) 13/2 1.25 (-0.94±0.59) 6/4 0.86 (-0.50±0.34) 8/3 1.25 -0.96±0.23 (-4.1)
本数を補正CVで(50-1200) 0.80 (-1.29±0.27) 13/3 1.39 (-0.80±0.62) 5/7 0.86 (-0.50±0.34) 8/3 1.39 -0.90±0.24 (-3.8)
本数を生CVで(50-1200) 4.34 (+2.25±0.67) 2/14 4.69 (+2.50±0.80) 1/10 4.59 (+3.24±0.52) 0/11 4.69 +2.62±0.39 (+6.7)
fold ごとの早期打ち切り 忍耐50(上限1200) 6.84 (+4.75±0.64) 0/16 7.05 (+4.86±0.58) 0/12 6.51 (+5.16±0.49) 0/12 7.05 +4.91±0.33 (+14.7)
fold ごとに val 最小(忍耐∞) 3.67 (+1.58±0.48) 2/14 3.90 (+1.71±0.59) 3/9 4.37 (+3.01±0.35) 0/12 4.37 +2.05±0.29 (+7.0)
葉も本数も補正CVで(4葉×24点) 1.05 (-1.05±0.44) 12/4 0.81 (-1.37±0.57) 8/4 0.78 (-0.57±0.45) 8/3 1.05 -1.00±0.28 (-3.6)
決定15 が選んだ葉: 低雑音 {31:1, 63:13, 127:2}・小得 {127:3, 255:9}・中雑音 {127:1, 255:11}(Day180 と同じ)
補正CV(600-1200) が選んだ本数: 低雑音 600〜1200(中央値 1050)・小得 600〜1200(中央値 700)・中雑音 600〜950(中央値 725)
=== 表3 本数方向の CV の偏り: (CV[a]−CV[b]) − (真[a]−真[b])(×1e-4、平均±SE)===
補正CV(葉31 / 63 / 127 / 255) 生CV(葉31 / 63 / 127 / 255)
[1200 対 600]
低雑音 +0.18±0.55 +0.05±0.46 +0.15±0.57 +0.36±0.35 +5.22 +5.88 +6.44 +6.19(SE 0.34〜0.59)
小得 -0.46±0.38 -0.09±0.53 -0.43±0.56 -0.15±0.40 +5.18 +7.40 +8.29 +8.44
中雑音 -0.30±0.34 +0.06±0.56 +0.16±0.39 -0.55±0.49 +5.95 +8.43 +10.17 +9.37
[300 対 600]
低雑音 -0.12±0.50 +0.10±0.52 -0.04±0.56 -0.15±0.46 -3.74 -4.45 -5.42 -6.03
小得 +0.25±0.46 +0.35±0.58 +0.09±0.40 +0.26±0.69 -3.44 -4.98 -6.88 -7.95
中雑音 +0.25±0.48 +0.28±0.54 -0.15±0.46 -0.11±0.61 -3.82 -5.66 -8.15 -9.41
(900 対 600 も補正CV は |平均| ≤ 0.40、全部 2SE 未満。生CV は +2.67〜+5.61)
=== 表4 fold ごとの早期打ち切り(上限1200): 止まる本数(中央値[fold の最小-最大])と、600本に対する真の差・CV の対の偏り ===
5本平均の山 忍耐50 の本数 真(忍耐50)−真(600) 補正CV の対の偏り 生CV の対の偏り
[低雑音] 葉63 1075 270[107-750] +4.99±0.60 -2.21±0.72 -6.38±0.91
葉127 725 233[101-494] +3.92±0.52 -2.49±0.87 -8.45±1.02
[小得] 葉127 1175 415[183-670] +5.21±0.57 -1.52±0.31 -4.95±0.55
葉255 650 296[189-613] +5.08±0.55 -1.63±0.57 -9.04±0.88
[中雑音] 葉127 1200 503[272-884] +3.44±0.59 -1.59±0.28 -3.47±0.44
葉255 700 342[210-533] +5.38±0.52 -1.80±0.57 -8.88±0.81
対の偏り = (CV_早期 − CV_600) − (真_早期 − 真_600)。負なら早期打ち切りを実際より良く見せる。補正CV は12組すべて負(-1.52〜-2.65)
忍耐∞(val 最小): 中雑音 葉255 で 393本、真 +3.53±0.46。葉31 でだけ 600本より良い(中雑音 -13.77、山が1,200本の葉)
=== 表5 費用(1ワールド5fold、中央値秒。M5 Pro・14並行。学習1,200本+予測は24点の塊+早期打ち切り2通り)===
葉31 学習 4.5 / 予測 31.4 葉63 6.2 / 45.0 葉127 9.5 / 69.6 葉255 16.9 / 118.8
(Day180 の 600本: 葉255 学習 8.4 / 予測 34.7。学習は本数にほぼ比例して2倍)
タスクC(分析と決定の模範)
- 決定18 の「600本」を「600〜1,200本から補正CV 最小」に替える(決定20、○)。 40ワールドまとめて −0.96±0.23(z −4.1、27/9)、最悪の損 2.19 → 1.25。低雑音は −1.31±0.26(5SE)で確か、小得 −0.94±0.59・中雑音 −0.50±0.34 は向きは同じだが 2SE 未満なので ◎ にしない。固定本数はどれも世界をまたいで持たない: 800本は小得・中雑音で 2SE 未満、1,200本は葉255 の世界で +1.9〜+3.0 悪化。理由は表1 で、本数の山は葉で大きく動く(葉31 は1,200本の先、葉255 は 400〜700本)。決定15 がどの葉を選ぶかは世界で変わるので、本数も葉に合わせて選ぶ必要がある。下限は 600本にする(50〜1,200 から選ぶと −0.90±0.24 でほぼ同じだが、50〜1,200 で 600本未満が選ばれた5ワールドのうち4つは下限600 の選択より +0.30〜+1.20 悪く、得をしたのは1つ(−0.24)だけ)
- 補正CV は 600 対 1,200本の比較にも使える(決定13 に追記、○)が、早期打ち切りの評価には使えない。 固定の本数どうしの比較では 1200 対 600 で偏り −0.55〜+0.36(24組すべて 2SE 未満)、生CV は +5.2〜+10.2(多い本数を悪く見せる)。一方、早期打ち切りは val のラベルを見て fold ごとに本数を決めるので、その val を OOF として使う CV は止めた点を有利に見る(選択の漏れ)。補正CV でも −1.5〜−2.7 楽観で、12組すべて負。val を見て何かを決めた候補は、その val での CV と比べない(決定13 の適用範囲に書く)
- 早期打ち切りを提出の本数決めに使わない(決定21、◎)。 忍耐50 は 40/40 で悪化(+4.91±0.33)。止まる本数は5本平均の山の 1/4〜2/3(中雑音 葉255: 342 対 700本)。1 fold のモデルは val の偶然に合わせて早く止まるが、提出は5本平均なので覚えすぎの分が平均で薄まり、山が後ろへずれる(Day180 の表6 と同じ構造)。忍耐∞(val 最小)でも +2.05±0.29
タスクC-3(CV設計書 決定1〜21、2026-10-14 版)
| # | 項目 | 今の決め方 | 印 | 根拠(測った数字) | 前提が変わったら |
|---|---|---|---|---|---|
| 1 | 分割 | ランダム KFold 5分割(shuffle・seed 固定) | △ | 敵対的 AUC 0.5004 だけ(Day163)。層別・Group と比べていない | 敵対的 AUC 0.55 超、または同一人物の行が複数あるなら Group/時間分割へ |
| 2 | 目的変数 | log1p で学習、expm1 で戻す | ◎ | private −0.0049、5/5 同じ向き(Day163) | 指標が RMSLE でなければ見直す |
| 3 | 元データ | 学習 fold にだけ足す | △ | −0.00024±0.00015(1.6SE、Day163) | 元データの分布が合成と違うなら測り直す |
| 4 | 予測のクリップ | 下限1 | — | NaN 防止の論理 | — |
| 5 | 公開LB | (撤回) | ✕ | 決定12 と矛盾(Day171) | — |
| 6 | 特徴量の採否 | 1つずつ足し、R2 で −2SE | ◎ | 28件中 26件が真と一致(Day164) | — |
| 7 | ハイパーパラメータ探索 | 既定値を trial 0、Optuna 60回、新分割 R2 で採否 | ○ | 2ワールドで 6/6(Day165)。葉・本数は決定15・20 が置き換え | — |
| 8 | 混ぜる | HistGB に knn 0.10 | ○ | 4ワールド中2つで得(Day166)。LightGBM の上では未計測 | 決定20 の1本の上で測り直す |
| 9 | 再現性 | 乱数なし・1スレッド、起動時に公開値と照合 | ◎ | M1→M5 Pro・venv をまたいで小数2桁一致(Day180)、今日 OOF 完全一致 | — |
| 10 | SE の使い分け | 採否は naive −2SE、報告は NB 補正 | ○ | 被覆 0.82(Day168)。NB 原典は未照合 | — |
| 11 | −2SE の読み方 | 「効く列か、効果±1.7以内の列」 | ○ | 108件中 11件採用(Day169) | — |
| 12 | 最終2本 | 1本目=決定20 の1本。2本目は未定 | △ | CV最良の損 2.80(Day170)、補正CV最良 0.92(Day171) | 公開LB と2本目の枠は持ち越し |
| 13 | CV の直し方 | 補正CV √(C² − A_test) で比べる。葉・本数の比較に使える。val を見て本数を決めた候補(早期打ち切り)には使えない | ◎(葉・固定本数)/✕(早期打ち切り) | 葉511 まで残り 2SE 以内(Day177)、本数 1200 対 600 で −0.55〜+0.36(今日)。早期打ち切りは −1.5〜−2.7 楽観(今日) | 指標が二乗誤差系でない、提出が fold 平均でないなら使えない |
| 14 | 乗り換えの余裕 m=2 | 乱数ありの候補だけ | 休眠 | 決定15 のあと発動0回(Day175〜177) | 乱数ありの候補を葉255 の上で作ったら測り直す |
| 15 | 葉の選び方 | lr0.1・300本で葉 31/63/127/255 を補正CV、端なら511 を足す | ◎ | 40ワールドで最悪の損 0.88(Day177)、学習率を変えても山の位置は同じ(Day178) | 学習率 0.02 以下では未計測 |
| 15b | min_child_samples | 既定の 20 | △ | mcs50 は 6/6 で悪化(Day176)。下げる方向は未計測 | 行数が1桁少ないデータで 10/20/50 を測る |
| 16 | 引き分けの扱い | 小さい木に寄せない(z=0) | ○ | 最悪 0.88 対 z=1 の 1.93(Day177) | 候補がほぼ同じ強さなら費用で寄せる |
| 17 | 差の書き方 | 差 ÷ 対の SE を添える。2SE 未満は決め手にしない。2ワールド以下は「判定不能」 | ○ | Day179 の +4.14(2ワールド)は16ワールドで +0.05(Day180) | — |
| 18 | 作り直しの本数 | 決定20 に置き換え(600本固定は廃止) | ✕(置き換え) | 600本固定は40ワールドで決定20 より +0.96±0.23 | — |
| 19 | 葉511 の本数選び | 休眠(決定20 に吸収: 葉511 なら範囲を 200〜600 に) | 休眠 | 葉511 の山 200〜450本(Day180) | 決定15 が葉511 を選ぶ世界が出たら |
| 20(新設) | 作り直しの1本 | 決定15 の葉を lr0.05・1,200本で1回学習し、600〜1,200本(50本刻み13点)から補正CV 最小の本数を採る | ○ | 対 600本固定 −0.96±0.23(z −4.1)、最悪の損 2.19→1.25。低雑音 5SE、小得・中雑音は 2SE 未満 | 小得・中雑音で 2SE を超えたら ◎。学習率を変えたら上限(1,200)を測り直す |
| 21(新設) | 早期打ち切り | 提出の本数決めに fold ごとの早期打ち切りを使わない | ◎ | 忍耐50 で 40/40 悪化(+4.91±0.33)、忍耐∞ でも +2.05±0.29 | 提出を5本平均でなく1本にするなら、山の位置が1本の曲線に戻るので測り直す |
タスクD(今日の決定の記録)
| # | 決定 | 採った案 | 採らなかった案と理由 | 前提が変わったら |
|---|---|---|---|---|
| 20(新設、○) | 作り直しの本数は 600〜1,200 から補正CV で選ぶ | 40ワールド −0.96±0.23、最悪 1.25。追加の費用は葉255 で学習 +8.5秒・13点の予測(1本だけ作るので小さい) | (a) 800本固定: −0.51±0.19 で得はあるが小得・中雑音で 2SE 未満、低雑音以外の世界で 3/9・4/8 と負け越し。(b) 1,200本固定: 葉255 の世界で +1.9〜+3.0 悪化。(c) 葉も本数も補正CV(4葉×24点): −1.00±0.28 で同じくらいだが、lr0.05・1,200本を4葉作る(1ワールド学習 37秒・予測 265秒、決定20 は選んだ1葉のみ)。差 −0.04 に4倍の費用は払わない。(d) 50〜1,200 から選ぶ: −0.90±0.24 で下限600 とほぼ同じ。600本未満が選ばれた5ワールド中4つで損(+0.30〜+1.20) | 学習率 0.02 以下では山がさらに後ろへ動くので、上限を 2,400 などに伸ばして測る。葉511 が選ばれたら範囲を 200〜600 にする(決定19 の休眠部分) |
| 21(新設、◎) | fold ごとの早期打ち切りを提出の本数決めに使わない | 40/40 悪化。止まる本数は5本平均の山の 1/4〜2/3 | (a) 忍耐を伸ばす(∞): +2.05±0.29 でまだ悪い。(b) 早期打ち切りの本数 × 係数(例 1.5倍): 山との比が葉・世界で 0.25〜0.65 とばらつき、1つの係数が無い(今日は係数を測っていない) | 提出が全データ再学習1本なら、1本の曲線の山が意味を持つので測り直す |
| 13(追記) | 補正CV の適用範囲に「val を見て決めた候補は ✕」を書く | 早期打ち切りの補正CV は 12組すべて −1.5〜−2.7 楽観 | 補正CV をそのまま全候補に使う: 早期打ち切りを良く見せ、40/40 で悪い候補を選びうる | 早期打ち切りを別の分割(入れ子)で決めるなら、その val は OOF に使わないので偏りは消えるはず(未計測) |
| 18(✕、置き換え) | 600本固定を廃止し、決定20 に置き換える | 決定20 より +0.96±0.23 | 600本を残し、低雑音だけ延ばす: 実データではどの世界か分からない | — |
📊 結果の可視化
1枚目: 本数を 600〜1,200 から補正CV で選ぶ決定20 は −0.96±0.23(z −4.1)。固定本数は 800本以外 2SE の得が無く、早期打ち切りは 40/40 で悪化。2枚目: 早期打ち切りは5本平均の山の 0.25〜0.65 の位置で止まる。
🪜 Step-by-Step 解説
Step 1: 再現を先に確かめる
1,200本のモデルの最初の600本は、600本のモデルと同じ木になるはず(乱数なし・同じ random_state)。40ワールドの 50〜600本の12点の OOF が Day180 の保存物と完全一致(差 0.0)し、truth の集計も 1.2e-9 以内(途中和を float32 で持った丸め)。これで「Day180 の続きを伸ばした」と言える。
Step 2: 葉ごとの山を見る(表1)
葉31・63 は小得・中雑音で1,200本でもまだ下がっている(−11〜−14)。葉255 は 400〜700本で山を越え、1,200本では +4〜+5 悪い。葉が4倍違うと山は2倍以上ずれる。
Step 3: 方針が選ぶ葉で見る(表2)
決定15 は小得・中雑音で葉255、低雑音で葉63 を選ぶ。だから「固定で何本」は、低雑音(葉63 の山 1,075本)と小得・中雑音(葉255 の山 650〜700本)を同時に満たせない。補正CV で本数を選ぶと、葉63 の世界では中央値 1,050本、葉255 の世界では 700〜725本を選び、3世界とも決定18 より良い向き。
Step 4: 補正CV が本数比較で偏らないか(表3)
1200 対 600 で 24組すべて 2SE 未満。生CV は多い本数を +5〜+10 悪く見せる(1本ずつの誤差は5本平均の得を測れない)。補正CV を本数選びに使う前提が 1,200本まで持つ。
Step 5: 早期打ち切りの真の損と、補正CV の盲点(表4)
忍耐50 は 40/40 で悪い。さらに、補正CV で「早期打ち切り 対 600本」を比べると、早期打ち切りを 1.5〜2.7 良く見せる。早期打ち切りは val の誤差が下がらなくなった点で止めるので、その val を OOF に使うと「たまたま val に合った点」を選んだ分だけ CV が良く出る。補正CV が消すのは「5本平均の得」の偏りで、「val で選んだ」偏りは消せない。
Step 6: CV設計書を書き直す
決定18 を ✕(置き換え)、決定20・21 を新設、決定13 に適用範囲を書き足した。決定12 の1本目を「決定20 の1本」に替えた。
📐 数学・統計の補足(文系向け)
なぜ葉が大きいと山が早く来るのか。 葉が多い木は1本でデータを細かく分ける。学習率 0.05 で少しずつ足していっても、細かい分け方ほど早く「偶然の凸凹」まで写し始める。葉31 の木は1本で覚える量が少ないので、1,200本足してもまだ「本当の形」を覚えている途中。
なぜ早期打ち切りは早すぎるのか。 1 fold のモデルは、val の誤差が一番低い点で止まる。ところが提出は5本の平均。5本は別々の行で学習しているので、覚えすぎた「偶然の凸凹」は5本でばらばらになり、平均すると打ち消し合う。だから5本平均では、1本で見たときより長く学習しても損をしない。1本の曲線で決めた本数は、5本平均には短すぎる。
なぜ補正CV でも早期打ち切りは良く見えるのか。 試験問題を見てから「どの勉強時間が一番点が取れたか」を選ぶと、その試験の点は実力以上に出る。早期打ち切りは val(試験)を見て本数を選んでいる。補正CV は「5本平均の得」を足し戻す道具で、「試験を見て選んだ」ずるは直せない。
🏆 Kaggleでの実践的な使い方
- 最終提出の本数は、fold ごとの early stopping で決めない。 公開ノートブックで最も多い書き方(
callbacks=[lgb.early_stopping(50)]で各 fold を止め、5本を平均)は、今日の40ワールドで最悪 7.05×10⁻⁴ の損(一番悪い方針)。LightGBM の early_stopping は「val が改善しなくなったら止める」機能で、提出が fold 平均であることは知らない(URL のみ記載、本文は今日照合していない) - 本数は「提出と同じ形(5本平均)」の補正CV で、葉ごとに選ぶ。 1回だけ上限まで学習し、
predict(start_iteration=…, num_iteration=…)で途中本数を取れば、学習は1回で済む - early stopping の CV スコアを他の候補と並べない。 val を見て止めた分だけ良く見える(今日は補正後でも 1.5〜2.7)。並べるなら、止める本数を別の分割で決める(入れ子)か、固定本数で作り直してから比べる
- 測った範囲の端が最良なら、端の先を測る。 Day180 の「600本が最良」は、測った上限が600本だっただけだった
⚠️ よくある誤解・ミス
| 誤解・ミス | なぜ起こるか | 正しい理解 |
|---|---|---|
| 「600本が最良」なら600本で十分 | 測った範囲の端と山を区別しない | 葉31〜127 の山は 600本の後ろ(〜1,200本の先)。端が最良なら端を伸ばす |
| 本数は多いほど安全(学習率が小さいから) | 小さい学習率は覚えすぎにくいと聞く | 葉255 は 400〜700本で山を越え、1,200本で +4〜+5 悪化 |
| fold ごとの early stopping は過学習を防ぐ定番 | 公開ノートブックの書き方 | 提出が5本平均なら止めるのが早すぎる。40/40 で悪化(+4.91) |
| early stopping の CV は他の候補と比べられる | 同じ OOF の形をしている | val を見て止めたので 1.5〜2.7(補正CV)/3.5〜10.9(生CV)良く見える |
| 補正CV はどんな比較にも使える | Day177〜180 で偏りが無かった | 消せるのは「5本平均の得」の偏りだけ。「val で選んだ」偏りは残る |
| 全部の葉×本数から選べば一番良い | 候補が多いほど良い物が入る | 決定20 との差は −0.04 で、費用は約4倍 |
🧭 扱わなかった範囲と理由
- 1,200本より先: 葉31・63 は小得・中雑音で 1,200本でもまだ下がっている。しかし決定15 はこの2つの世界で葉31・63 を選ばない(葉127/255)ので、方針には効かない。低雑音の葉63 の山は 1,075本(16ワールド中 4つが 1,200本で最良)で、上限の影響は小さいと見たが、2,400本までは測っていない
- 早期打ち切りの本数を別の分割で決める案(入れ子): 決定21 で早期打ち切りをやめたので、今日は測らなかった。補正CV の楽観が入れ子で消えるかは未計測
- 早期打ち切り × 係数(例 1.5倍): 山との比が 0.25〜0.65 とばらつくので見送ったが、係数ごとの損は計算していない
- 葉511: 今日は作らず、格子の最良にだけ Day180 の12点(〜600本)を入れた
- 持ち越し: 学習率 0.02 以下、50〜150本での補正CV の偏り、seed を変えた独立な10分割、mcs 5・10、乱数ありの候補(決定14)、候補数 K と選択の上振れ、系統別の m(et)、公開LB と2本目の枠(決定12)、avg3、OOF側とtest側の seed の揺れ、noKeytel・raw7、入れ子CV との比較、Day170 の出し直し、Day165 の SE 個数差、Day162 の選択バイアス、真の効果0の偽採用率
- 原典照合: Nadeau & Bengio・Krogh & Vedelsby・LightGBM の文書は未照合。LightGBM 4.7.0 の
eval_setの非推奨警告は今日も出た(結果には影響しない) - 実データ(S5E5): 縮小シミュレータの上の数字のみ。Kaggle のログインとルール同意が要るので、利用者の作業として残す
🚀 次のステップ
- 発展: 決定20 の1本の上で、決定8(knn を混ぜる)がまだ得かを測り直す。混ぜる相手が強くなると、混ぜる得は小さくなるはず
- 発展: 学習率 0.02・上限 3,000本で、決定20 の範囲(600〜1,200)がどこへ動くか
- 次回予告: Day 182 — Playground Series 実戦投入⑰(今月のまとめに向けて、決定20 の1本を基準に「最終2本」(決定12 の2本目の枠)を決める。公開LB 20% を2本目の選択に使うと得か損かを40ワールドで測る)。月替わりテーマ3「画像(CV)系コンペへの応用」は 10/25 から
📝 自己評価(解いた後に記入)
自分の回答・気づき・メモ: