Day 181 — 2026-10-14 | Playground Series 実戦投入⑯ — 1,200本まで伸ばすと、葉31〜127 の5本平均の山は600本の後ろ(ほぼ全ワールド)、葉255 は低雑音 400・小得 650・中雑音 700本。作り直しの本数を 600〜1,200 から補正CV で選ぶと決定18(600本固定)より 40ワールドで −0.96±0.23(z −4.1、最悪の損 2.19→1.25)。fold ごとの早期打ち切りは 40/40 で悪化(+4.91±0.33)し、補正CV でもその悪さを 1.5〜2.7 見逃す(×10⁻⁴)

2026-10-14 金 / Phase 6(月替わりテーマ2: Playground Series 実戦投入) コーディング→分析 40ワールド・800 fit(各1,200本)・壁時計15分(M5 Pro・14並行)

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

ここまでの決め方(今日の前の CV設計書の該当部分)。

決定中身印(今日の前)
13候補は補正CV √(C² − A_test) で比べる。C は生の OOF RMSLE、A_test は test 2万行での5本の予測の分散(ラベル不要)葉511 まで ◎、本数比較(200 対 600)○
15lr0.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 の作り直し)
途中本数 n50, 100, …, 1,200 の24点。学習し直さず塊ごとに予測を足す
T55本平均の 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_kcalint / float(9列)Day163〜180 と同じ合成(ワールドの乱数 3000+番号)。低雑音16・小得12・中雑音12全候補
oof5(4, 24, 30000) float32lr0.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・G1Day180 の決定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世界を同時に満たせない。

5本平均の真の RMSLE − 同じ葉の600本(×10⁻⁴、下ほど良い、ワールド平均)大きい丸=その曲線の最良。600本(Day180 の上限)の右に山がある。上端 +9 で切った-15-10-5+0+5Day180 の上限 600本300500600700800900100011001200━ 低雑音 葉63(決定15 が13/16 で選ぶ)━ 小得 葉255(9/12 で選ぶ)━ 中雑音 葉255(11/12 で選ぶ)╍ 中雑音 葉31(選ばれない)

❓ 問題

タスクA(予想を先に書く)

測る前に、次の3つに「当たる/外れる」と理由を書く。

  1. 葉31〜127 の5本平均の山は600本より後ろにある。葉255 は600本付近
  2. 1,200本に固定すれば、600本固定(決定18)より3世界とも良くなる
  3. fold ごとの早期打ち切り(忍耐50、上限1,200)は、600本固定より悪い。補正CV ならその悪さを見抜ける

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

  1. day180.py の生成部を1文字も変えずに、lr0.05・1,200本の葉31〜255 を作り、24点の途中本数と、忍耐50・忍耐∞の早期打ち切りの予測を1回の学習から取る day181.py を書く
  2. 600本までの12点が Day180 の保存物と一致することを確かめる(再現)
  3. analyze181.py で、本数曲線(〜1,200本)・方針全体の損・本数方向の CV の偏り(1200 対 600、900 対 600、300 対 600)・早期打ち切りの本数と真の損・費用を出す

タスクC(分析と決定)

  1. 決定18 の「600本」を何に替えるか(固定本数を延ばす/本数を CV で選ぶ/早期打ち切り)。候補ごとに対の差と最悪の損を並べて決める
  2. 補正CV(決定13)は 600 対 1,200本の比較にも使えるか。早期打ち切りの候補の評価にも使えるか
  3. 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(判定)

  1. 前半は当たり、後半は世界で分かれる。 葉31・63 は小得・中雑音で 12/12 が1,200本で最良(まだ下がり続けている)。葉127 も 600本の後ろ(低雑音 11/16、小得・中雑音 12/12)。葉255 は低雑音で 400本(0/16 が600本より後ろ)、小得 650本(7/12)、中雑音 700本(11/12)で、600本の少し後ろ
  2. 外れ。 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 未満)
  3. 前半は当たり(大きく悪い)、後半は外れ。 忍耐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(分析と決定の模範)

  1. 決定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)だけ)
  2. 補正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 の適用範囲に書く)
  3. 早期打ち切りを提出の本数決めに使わない(決定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 完全一致—
10SE の使い分け採否は 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本目の枠は持ち越し
13CV の直し方補正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 以下では未計測
15bmin_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.23600本を残し、低雑音だけ延ばす: 実データではどの世界か分からない—

📊 結果の可視化

40ワールド(低雑音16・小得12・中雑音12)、800 fit(各1,200本)。50〜600本は Day180 の保存物と OOF が完全一致。
決定18(600本固定)との対の差(×10⁻⁴、左ほど良い、ひげ=±2SE、40ワールドまとめ)緑=ひげが0 を越えない(2SE 超で良い)、赤=2SE 超で悪い、灰=判定不能-1+1+2+3+4+5葉も本数も補正CV(4葉×24点)-1.00±0.28本数を補正CV(600〜1200)=決定20-0.96±0.23800本固定-0.51±0.191000本固定+0.01±0.36決定15 のまま(lr0.1)+0.72±0.141200本固定+1.05±0.52fold ごとに val 最小(忍耐∞)+2.05±0.29本数を生CVで+2.62±0.39fold ごとの早期打ち切り(忍耐50)+4.91±0.33
fold ごとの早期打ち切り(忍耐50)が止まる本数 対 5本平均の山(中央値)青=早期打ち切り、橙=5本平均の真の山。提出は5本平均なので、1本の曲線で止めると早すぎる0本300本600本900本1200本低雑音 葉632701075(比 0.25)低雑音 葉127233725(比 0.32)小得 葉1274151175(比 0.35)小得 葉255296650(比 0.46)中雑音 葉1275031200(比 0.42)中雑音 葉255342700(比 0.49)

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 から

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

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