Day 175 — 2026-10-08 | Playground Series 実戦投入⑩ — 乱数なしの葉 31/63/127 から補正CVで基準を選ぶ決定15 は、3種類の世界で最悪の超過損 0.50(決定14 のままだと 25.24、×10⁻⁴)。低雑音でも葉を増やす方が良く(leaf63 −3.67、16/16)、基準が強くなった結果、決定14 の余裕 m は 0〜6 のどれでも1回も発動しなかった

2026-10-08 金 / Phase 6(月替わりテーマ2: Playground Series 実戦投入) コーディング→分析 低雑音16+小得12+中雑音12ワールド · 乱数なし3本 · 600 fit · 約684秒

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

Day174 のおさらい。 小得ワールド(雑音 0.12/0.08)で、葉の数と抜き出しを 2×2 で分けた。

抜き出しなし抜き出し 0.7
葉 31lgb_det(基準 0)b7 +5.79
葉 63leaf63 −12.72leaf63_b7 −2.47

効いていたのは葉の数だけで、余裕 m の調整(0.1〜0.2)より候補の中身(12.72)が約 60 倍効いた。そこで決定15 を置いた。

決定15(Day174 新設・印 △): 基準は lgb_det に固定しない。乱数なしの候補(葉 31・63 など)を補正CVで比べ、良い方を基準にする。そのあと決定14(基準より補正CVが 2×10⁻⁴ 以上良い乱数ありの候補だけに乗り換える)を使う

△ の理由は2つ。(1) 低雑音・中雑音に leaf63 が無かった。(2) 低雑音で leaf63_b7 は +5.44 と悪かったので、「低雑音では葉を増やすと悪くなる」かもしれず、そうなら決定15 は「正しく選び分けられるか」が問われる。今日はこの2つを測る。

用語整理(Day174 と同じものは省略):

用語意味
乱数なしの候補(DET3)lgb_det(葉31)・leaf63・leaf127。bagging も列抽出もしないので model seed に依らず同じ予測。seed 組 s0・s1 で同じ値を使う
全14本Day172 の11本(乱数あり9本+hgb_default+lgb_det)+leaf63+leaf127。小得にある b7 は低雑音・中雑音に無いので外した
損(regret)選んだ候補の真の RMSLE − 全14本の中の真の最良(×10⁻⁴)。真は 6万行の truth で5 fold モデル平均の予測を採点したもの
補正CV決定13: √(CV² − A_test)。A_test は test 行の fold 間の予測のばらつき(ラベル不要)。1モデルの OOF と5モデル平均の提出の差を引く
最悪の超過損3種類の世界それぞれで「その世界で一番良い方針」との差を取り、3つのうち最大。どの世界か分からないときの備え

数値はすべて縮小シミュレータの上の実測。実データ(S5E5)で同じ大きさになるとは限らない。

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

名前形・型意味・作り方使う場所
Sex … Body_Temp, Dur_x_HR, Keytel_kcalint / float(9列)Day163〜174 と同じ合成(ワールドの乱数 3000+番号)。Calories の雑音だけ 0.05/0.03(低)・0.12/0.08(小得)・0.15/0.10(中)全候補
raw175_w*.npz(low/mid/sm)oof (3,30000)・evk (3,5,80000) float32今日作った乱数なし3本(lgb_det・leaf63・leaf127)。40 ワールド × 3本 × 5 fold = 600 fit基準選び・再現テスト
raw172 / raw173 / raw174oof (11〜13, seed, 30000)・evk (…, 5, 80000)Day172〜174 の保存物。乱数ありの候補はここから読む(seed 組 s0・s1)決定14 の乗り換え先
C / Ca / T5float(RMSLE)生の CV(1モデルの OOF)/補正CV √(C²−A_test)/真(6万行 truth で5 fold 平均を採点)表1〜5
regretfloat(×10⁻⁴)選んだ1本の T5 − 全14本の中の最良 T5表3・表5

評価指標(RMSLE)と、生の CV の偏り

RMSLE は log1p 空間の RMSE(小さいほど良い)。生の CV は1モデルの OOF で採点するが、提出は5モデルの平均なので、1本ごとの予測がばらつく候補(葉が多い木)ほど不利に見える。下の図は、葉 63・127 で生の CV(赤)が真の差より何だけ悪く見せたかと、補正CV(緑)で残った偏り。

CV が葉の多い候補をどれだけ悪く見せるか(CV差 − 真の差、×10⁻⁴、右=悪く見せる)赤=生の CV(1モデルの OOF)/緑=補正CV(決定13)。各行の3本は 低雑音・小得・中雑音051015leaf63+5.69+0.33低雑音+5.99-0.51小得+6.24-0.86中雑音leaf127+13.96+2.04低雑音+15.91+1.20小得+16.58-0.00中雑音

❓ 問題

タスクA(予想)— コードを書く前に

次の3つを数字で予想して書く(外れてよい。外れ方が今日の学び)。

  1. 低雑音ワールドで leaf63・leaf127 は lgb_det より良いか悪いか(Day172 で leaf63_b7 は +5.44)
  2. 生の CV(補正なし)は、葉を増やした候補を良く見せるか悪く見せるか。その大きさは葉の数とともにどう動くか
  3. 決定15 を入れたあと、決定14 の余裕 m(0〜6)で損は変わるか

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

  1. day174.py の生成部(ワールドの乱数 3000+番号・雑音だけ引数)をそのまま使い、乱数なしの3本だけを作る day175.py を書く。今日の lgb_det が保存物(raw172/173/174)の lgb_det と完全一致することを、OOF と fold 別の test 予測の両方で確かめる(決定9)。小得では leaf63 も raw174 と照合できる
  2. 低雑音(0.05/0.03)16、中雑音(0.15/0.10)12、小得(0.12/0.08)12 を回す
  3. 集計 analyze175.py で、(a) 真の差、(b) 基準選びの的中と損、(c) 方針ごとの損と最悪の超過損、(d) ワールド対の差を出す

タスクC(分析)

  1. 生の CV と補正CV は、葉の数を増やした候補の差をそれぞれどれだけずらすか(残りの偏り)
  2. 低雑音で決定15 の損 0.88 はどこから来るか。「常に leaf63」(0.38)とどちらを採るべきか
  3. 決定14・15 の印をどう付け替えるか。決定14 の m はこの先も要るか

💡 ヒント

ヒント1(方向性)

新しく fit するのは乱数なしの3本だけでよい。乱数ありの候補の予測は保存してあり、同じワールドを同じ乱数で作れば、行の順番もラベルも一致する(ytr・yev を np.array_equal で確かめる)。

ヒント2(アプローチ)

生の CV の偏りは「1モデルの OOF」と「5モデル平均の提出」の差(Day171)。葉が多いほど1本ごとの予測がばらつくので、5本平均の得は大きくなるはず。A_test(ラベル不要)を候補ごとに見れば、偏りの大きさを先に予想できる。

ヒント3(コード骨格)

DET = [("lgb_det", {}), ("leaf63", dict(num_leaves=63)), ("leaf127", dict(num_leaves=127))]
for k, (tr, va) in enumerate(KFold(5, shuffle=True, random_state=0).split(train)):
    ...
    for j, (nm, over) in enumerate(DET):
        m = lgb.LGBMRegressor(**{**LG, **over}, random_state=k).fit(X, y)   # 乱数なしなので seed は形だけ
        oof[j, va] = m.predict(XV); evk[j, k] = m.predict(XE)

# 決定15: 基準 = 乱数なし3本の補正CV最良 → 乱数あり9本+hgb の補正CV最良が m 以上良ければ乗り換え
base = h.Ca[DET3].idxmin(); b = h.Ca[RAND].idxmin()
pick = b if (h.Ca[base] - h.Ca[b]) * 1e4 > m else base

✅ 模範解答

タスクA(予想の模範と、実測との照合)

予想模範の予想(書いた時点)実測当たり外れ
1. 低雑音で葉を増やすと悪くなる(leaf63_b7 +5.44 から)leaf63 −3.67±0.34(16/16 で良い)、leaf127 −3.50±0.50(15/16)外れ。+5.44 は抜き出し 0.7 の害(Day174 の b7 +5.79)で、葉の数は低雑音でも効いた
2. 生の CV の偏り葉が多いほど悪く見せる(5本平均の得を数えないため)生の偏り leaf63 +5.69〜+6.24、leaf127 +13.96〜+16.58。補正後は −0.86〜+2.04方向は当たり。大きさは葉 63 で約 6、127 で約 15 と、葉の数にほぼ比例して増えた
3. 決定15 のあと m で損は変わるか少し変わるm=0〜6 で損が完全に同じ(3世界とも)。乗り換えが1回も起きなかった外れ。基準が leaf63/leaf127 になると、乱数ありの候補(葉31か leaf63_b7)はどれも補正CVで基準に届かない

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

day175.py(生成部は day174.py と1文字も変えていない。変えたのは候補の一覧と保存形式だけ):

"""Day175: Day172〜174 と同じ生成器(seed 3000+W、雑音だけ引数)で、乱数なしの LightGBM 3本(lgb_det=葉31・leaf63・leaf127)を作る。
lgb_det は保存物(raw172/173/174)と一致するか確かめる用(決定9)。leaf63 は小得では raw174 とも照合できる。
使い方: OMP_NUM_THREADS=1 python day175.py <world> <noise_orig> <noise_syn>  -> raw175_w<world>.npz
"""
import sys, time, hashlib
import numpy as np, pandas as pd
import lightgbm as lgb
from sklearn.ensemble import HistGradientBoostingRegressor
from sklearn.model_selection import KFold

T0 = time.time()
W = int(sys.argv[1])
NO, NS = float(sys.argv[2]), float(sys.argv[3])
N_ORIG, N_TRAIN, N_TEST, N_TRUTH = 15000, 30000, 20000, 60000


def keytel_rate(sex, hr, w, age):
    m = (-55.0969 + 0.6309 * hr + 0.1988 * w + 0.2017 * age) / 4.184
    f = (-20.4022 + 0.4472 * hr - 0.1263 * w + 0.074 * age) / 4.184
    return np.where(sex == 1, m, f)


def make_original(n, rng):  # Day163-172 と同じ(雑音 NO だけ引数化)
    sex = rng.integers(0, 2, n); age = rng.integers(20, 80, n)
    h = np.where(sex == 1, rng.normal(175, 7, n), rng.normal(162, 6.5, n)).round()
    w = (rng.normal(24.5, 2.5, n) * (h / 100) ** 2).round()
    dur = rng.integers(1, 31, n).astype(float)
    hr = np.clip(80 + 1.2 * dur + rng.normal(0, 5, n), 67, 128).round()
    temp = (37 + 0.35 * dur ** 0.5 + rng.normal(0, 0.2, n)).round(1)
    rate = np.clip(keytel_rate(sex, hr, w, age), 0.3, None)
    cal = np.clip((rate * dur * np.exp(rng.normal(0, NO, n))).round(), 1, None)
    return pd.DataFrame(dict(Sex=sex, Age=age, Height=h, Weight=w, Duration=dur,
                             Heart_Rate=hr, Body_Temp=temp, Calories=cal))


def synth_from(orig, n, rng):  # Day163-172 と同じ(雑音 NS だけ引数化)
    s = orig.sample(n, replace=True, random_state=int(rng.integers(1 << 30))).reset_index(drop=True)
    s["Age"] = np.clip((s.Age + rng.normal(0, 1.5, n)).round(), 20, 79)
    s["Height"] = (s.Height + rng.normal(0, 2, n)).round()
    s["Weight"] = (s.Weight + rng.normal(0, 2, n)).round()
    s["Duration"] = np.clip((s.Duration + rng.normal(0, 1.0, n)).round(), 1, 30)
    s["Heart_Rate"] = (s.Heart_Rate + rng.normal(0, 2, n)).round()
    s["Body_Temp"] = (s.Body_Temp + rng.normal(0, 0.1, n)).round(1)
    s["Calories"] = np.clip((s.Calories * np.exp(rng.normal(0, NS, n))).round(), 1, None)
    return s


def add_feats(d):
    d = d.copy()
    d["Dur_x_HR"] = d.Duration * d.Heart_Rate
    d["Keytel_kcal"] = np.clip(keytel_rate(d.Sex.values, d.Heart_Rate.values, d.Weight.values, d.Age.values), 0.3, None) * d.Duration
    d["log_Dur"] = np.log(d.Duration)
    return d


RAW = ["Sex", "Age", "Height", "Weight", "Duration", "Heart_Rate", "Body_Temp"]
BASE = RAW + ["Dur_x_HR", "Keytel_kcal"]                    # 決定6
LG = dict(objective="regression", learning_rate=0.1, n_estimators=300, num_leaves=31, min_child_samples=20,
          n_jobs=1, verbose=-1)
# Day175: 乱数なしの候補だけを作る(seed 1組で十分)。lgb_det は保存物との一致確認用
DET = [("lgb_det", {}), ("leaf63", dict(num_leaves=63)), ("leaf127", dict(num_leaves=127))]
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)
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):
        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)
    print(f"world {W} fold {k} {time.time()-T0:.0f}s", flush=True)
assert names == ["lgb_det", "leaf63", "leaf127"]          # 決定9: 名前と行の対応
np.savez_compressed(f"raw175_w{W}.npz", oof=oof, evk=evk, ytr=train.Calories.values,
                    yev=evalset.Calories.values, names=np.array(names))
print(f"world {W} wall {time.time()-T0:.0f}s")

analyze175.py:

"""Day175 集計: 決定15(基準を乱数なしの候補 lgb_det/leaf63/leaf127 から補正CVで選び、その後に決定14 の余裕 m で乱数ありへ乗り換え)を
低雑音(raw172 w0..15)・小得(raw174 sm w0..11)・中雑音(raw173 mid w0..11)で測る。乱数なしの3本は今日の raw175。
使い方: python analyze175.py <d172> <d173mid> <d174sm>   (カレントに low/ mid/ sm/ の raw175 がある前提)
"""
import sys, glob
import numpy as np, pandas as pd

N = 20000
MS = [0, 1, 2, 3, 4, 6]
POOL11 = ["hgb_default", "lgb_det", "bag08", "bag05", "ff05", "bag05_ff05", "bag03_ff05",
          "leaf63_b7", "leaf15_b7", "lr05_it600_b7", "et"]
DET3 = ["lgb_det", "leaf63", "leaf127"]
KINDS = ("低雑音", "小得", "中雑音")
SRC = {"低雑音": (sys.argv[1], "raw172", "low"), "中雑音": (sys.argv[2], "raw173", "mid"), "小得": (sys.argv[3], "raw174", "sm")}


def sle(y, lp):
    return (np.log1p(np.clip(np.expm1(lp), 1, None)) - np.log1p(y)) ** 2


rows, rep = [], []
for kind, (d, pre, sub) in SRC.items():
    for f in sorted(glob.glob(f"{d}/{pre}_w*.npz"), key=lambda f: int(f.split("_w")[1].split(".")[0])):
        w = int(f.split("_w")[1].split(".")[0])
        z = np.load(f); O, E, ytr, yev, names = z["oof"], z["evk"].astype(np.float64), z["ytr"], z["yev"], list(z["names"])
        assert names[:11] == POOL11
        n = np.load(f"{sub}/raw175_w{w}.npz"); O2, E2, n2 = n["oof"], n["evk"].astype(np.float64), list(n["names"])
        assert n2 == DET3 and np.array_equal(n["ytr"], ytr) and np.array_equal(n["yev"], yev)
        j = names.index("lgb_det")
        r = dict(kind=kind, world=w, lgb_oof=np.abs(O2[0] - O[j, 0]).max(), lgb_ev=np.abs(E2[0] - E[j, 0]).max())
        if "leaf63" in names:
            j2 = names.index("leaf63"); r.update(l63_oof=np.abs(O2[1] - O[j2, 0]).max(), l63_ev=np.abs(E2[1] - E[j2, 0]).max())
        rep.append(r)
        ytru = yev[N:]
        for s in (0, 1):
            for j, nm in enumerate(names[:11]):
                c = sle(ytr, O[j, s]).mean(); a = E[j, s, :, :N].var(axis=0).mean(); t5 = sle(ytru, E[j, s].mean(0)[N:]).mean()
                rows.append(dict(kind=kind, world=w, st=f"s{s}", cand=nm, C=np.sqrt(c), Ca=np.sqrt(c - a), T5=np.sqrt(t5)))
            for j, nm in enumerate(DET3[1:], start=1):
                c = sle(ytr, O2[j]).mean(); a = E2[j, :, :N].var(axis=0).mean(); t5 = sle(ytru, E2[j].mean(0)[N:]).mean()
                rows.append(dict(kind=kind, world=w, st=f"s{s}", cand=nm, C=np.sqrt(c), Ca=np.sqrt(c - a), T5=np.sqrt(t5)))
D = pd.DataFrame(rows); RP = pd.DataFrame(rep)
print("=== 表0 再現(決定9): 今日の lgb_det/leaf63 と保存物の最大絶対差 ===")
print(RP.groupby("kind")[[c for c in RP.columns if c not in ("kind", "world")]].max().to_string())

P = D.pivot_table(index=["kind", "world", "st"], columns="cand", values="T5")
R = (P.sub(P["lgb_det"], axis=0) * 1e4)
Q = D.pivot_table(index=["kind", "world", "st"], columns="cand", values="Ca")
RC = (Q.sub(Q["lgb_det"], axis=0) * 1e4)
PC = D.pivot_table(index=["kind", "world", "st"], columns="cand", values="C")
RR = (PC.sub(PC["lgb_det"], axis=0) * 1e4)
print("\n=== 表1 真の差 T5−T5(lgb_det)(×1e-4、ワールド平均±SE)/良い割合 ===")
for kind in KINDS:
    g = R.xs(kind).groupby("world").mean()
    out = [f"{c} {g[c].mean():+.2f}±{g[c].std(ddof=1)/np.sqrt(len(g)):.2f} ({(g[c]<0).mean():.2f})" for c in ["leaf63", "leaf127", "leaf63_b7", "hgb_default"]]
    print(f"[{kind}] " + "  ".join(out))
print("\n=== 表1b 乱数なし3本の 生CV差・補正CV差・真の差(×1e-4、ワールド平均)とずれのSD ===")
for kind in KINDS:
    x = [kind]
    for c in ["leaf63", "leaf127"]:
        g = pd.DataFrame(dict(raw=RR.xs(kind)[c], cor=RC.xs(kind)[c], tru=R.xs(kind)[c])).xs("s0", level="st")
        x.append(f"{c}: 生 {g.raw.mean():+.2f} 補正 {g.cor.mean():+.2f} 真 {g.tru.mean():+.2f} ずれSD {(g.cor-g.tru).std(ddof=1):.2f} 生ずれSD {(g.raw-g.tru).std(ddof=1):.2f}")
    print("  ".join(x))

print("\n=== 表2 基準選び(乱数なし3本の中で補正CV最良を選ぶ): 的中と損(×1e-4)===")
for kind in KINDS:
    x = D[(D.kind == kind) & (D.st == "s0") & D.cand.isin(DET3)]
    hit, reg, picks, best = [], [], [], []
    for w, h in x.groupby("world"):
        h = h.set_index("cand"); p = h.Ca.idxmin(); b = h.T5.idxmin()
        hit.append(p == b); reg.append((h.T5[p] - h.T5[b]) * 1e4); picks.append(p); best.append(b)
    print(f"[{kind}] 的中 {sum(hit)}/{len(hit)} 損 {np.mean(reg):.2f} 最大 {np.max(reg):.2f}  選んだ {pd.Series(picks).value_counts().to_dict()}  真の最良 {pd.Series(best).value_counts().to_dict()}")
    rg = pd.Series(reg); print(f"     生CVで選ぶと: ", end="")
    hit2, reg2 = [], []
    for w, h in x.groupby("world"):
        h = h.set_index("cand"); p = h.C.idxmin(); b = h.T5.idxmin(); hit2.append(p == b); reg2.append((h.T5[p] - h.T5[b]) * 1e4)
    print(f"的中 {sum(hit2)}/{len(hit2)} 損 {np.mean(reg2):.2f}")

ALL = POOL11 + ["leaf63", "leaf127"]
RAND = [c for c in POOL11 if c != "lgb_det"]


def policy(h, name, m):
    if name == "常にlgb_det": return "lgb_det"
    if name == "常にleaf63": return "leaf63"
    if name == "全14本の補正CV最良": return h.Ca.idxmin()
    if name == "決定15の基準だけ": return h.Ca[DET3].idxmin()
    base = "lgb_det" if name == "決定14" else h.Ca[DET3].idxmin()
    b = h.Ca[RAND].idxmin()
    return b if (h.Ca[base] - h.Ca[b]) * 1e4 > m else base


rows = []
for (kind, w, st), h in D.groupby(["kind", "world", "st"]):
    h = h.set_index("cand"); best = h.T5[ALL].min()
    for name in ["決定14", "決定15", "決定15の基準だけ", "全14本の補正CV最良", "常にlgb_det", "常にleaf63"]:
        for m in (MS if name in ("決定14", "決定15") else [None]):
            p = policy(h, name, m)
            rows.append(dict(kind=kind, world=w, st=st, pol=name if m is None else f"{name} m={m}", pick=p, regret=(h.T5[p] - best) * 1e4))
G = pd.DataFrame(rows)
print("\n=== 表3 方針ごとの損(全14本の真の最良との差、×1e-4、ワールド平均±SE)と3世界の最悪の超過損 ===")
L = G.groupby(["pol", "kind"]).regret.mean().unstack("kind")[list(KINDS)]
SEt = G.groupby(["pol", "kind", "world"]).regret.mean().groupby(["pol", "kind"]).agg(lambda s: s.std(ddof=1) / np.sqrt(len(s))).unstack("kind")[list(KINDS)]
X = L - L.min()
T = L.round(2).astype(str) + "±" + SEt.round(2).astype(str)
T["最悪の超過"] = X.max(axis=1).round(2)
print(T.sort_values("最悪の超過").to_string())
print("\n=== 表4 選ばれた候補(m=2)と真の最良 ===")
for kind in KINDS:
    for pol in ["決定14 m=2", "決定15 m=2"]:
        x = G[(G.kind == kind) & (G.pol == pol)]
        print(f"[{kind}] {pol}: {x.pick.value_counts().to_dict()}")
    bc = D[(D.kind == kind) & D.cand.isin(ALL)].loc[lambda d: d.groupby(['world', 'st']).T5.idxmin()].cand.value_counts().to_dict()
    print(f"[{kind}] 真の最良: {bc}")
print("\n=== 表5 ワールド対の差 決定15 m=2 − 決定14 m=2(×1e-4)===")
for kind in KINDS:
    W = G[G.kind == kind].groupby(["world", "pol"]).regret.mean().unstack()
    for a, b in [("決定15 m=2", "決定14 m=2"), ("決定15 m=2", "常にleaf63"), ("決定15 m=2", "決定15の基準だけ"), ("決定15 m=0", "決定15 m=2")]:
        d = W[a] - W[b]; print(f"[{kind}] {a} − {b}: {d.mean():+.2f}±{d.std(ddof=1)/np.sqrt(len(d)):.2f}")
G.to_pickle("pol175.pkl"); D.to_pickle("dec175.pkl")

extra175.py(残りの偏り・葉 63 対 127・単純な方を優先する版):

"""Day175 追加: 乱数なし3本の補正CVの残りの偏り(補正差−真の差)と、低雑音の leaf63 対 leaf127 の真の差、A_test の大きさ"""
import numpy as np, pandas as pd
D = pd.read_pickle("dec175.pkl"); D = D[D.st == "s0"]
for k in ("低雑音", "小得", "中雑音"):
    P = {v: D[D.kind == k].pivot(index="world", columns="cand", values=v) for v in ("C", "Ca", "T5")}
    for c in ("leaf63", "leaf127"):
        e = ((P["Ca"][c] - P["Ca"]["lgb_det"]) - (P["T5"][c] - P["T5"]["lgb_det"])) * 1e4
        r = ((P["C"][c] - P["C"]["lgb_det"]) - (P["T5"][c] - P["T5"]["lgb_det"])) * 1e4
        print(f"[{k}] {c}: 補正の残り偏り {e.mean():+.2f}±{e.std(ddof=1)/np.sqrt(len(e)):.2f}  生の偏り {r.mean():+.2f}±{r.std(ddof=1)/np.sqrt(len(r)):.2f}")
    a = {c: ((P["C"][c] ** 2 - P["Ca"][c] ** 2) * 1e4).mean() for c in ("lgb_det", "leaf63", "leaf127")}
    print(f"[{k}] A_test(×1e-4, 二乗の単位)", {c: round(v, 3) for c, v in a.items()})
    d = (P["T5"]["leaf127"] - P["T5"]["leaf63"]) * 1e4; dc = (P["Ca"]["leaf127"] - P["Ca"]["leaf63"]) * 1e4
    print(f"[{k}] 真 leaf127−leaf63 {d.mean():+.2f}±{d.std(ddof=1)/np.sqrt(len(d)):.2f}  補正CV {dc.mean():+.2f}  符号一致 {(np.sign(d)==np.sign(dc)).sum()}/{len(d)}  相関 {np.corrcoef(d, dc)[0,1]:.2f}")
print("\n--- 単純な方を優先する版(葉 31→63→127 の順に、補正CVが m' 以上良いときだけ大きい方へ進む): 全14本の最良との損(×1e-4、s0/s1 平均)---")
D = pd.read_pickle("dec175.pkl")
rows = []
for (k, w, st), h in D.groupby(["kind", "world", "st"]):
    h = h.set_index("cand"); best = h.T5.drop(["b7"], errors="ignore").min()
    for mp in [0, 1, 2, 3, 4]:
        cur = "lgb_det"
        for nx in ("leaf63", "leaf127"):
            if (h.Ca[cur] - h.Ca[nx]) * 1e4 > mp: cur = nx
        rows.append(dict(kind=k, world=w, mp=mp, regret=(h.T5[cur] - best) * 1e4, pick=cur))
R = pd.DataFrame(rows)
L = R.groupby(["mp", "kind"]).regret.mean().unstack()[["低雑音", "小得", "中雑音"]]
L["最悪の超過(決定15 m'=0 の 0.88/0/0 を含む表での)"] = (L - L.min()).max(axis=1)
print(L.round(2).to_string())
print(R[R.kind == "低雑音"].groupby("mp").pick.value_counts().unstack().fillna(0).astype(int).to_string())

実行(run.sh は Day174 と同じく DYLD_LIBRARY_PATH に scikit-learn 同梱の libomp を入れ、day175.py <w> <雑音元> <雑音合成> を出力先ディレクトリで呼ぶだけ):

# 40 ワールドを 4 並行で作る(低雑音 16・中雑音 12・小得 12)
(for w in $(seq 0 15); do echo "$w 0.05 0.03 low"; done; for w in $(seq 0 11); do echo "$w 0.15 0.10 mid"; echo "$w 0.12 0.08 sm"; done) | xargs -P 4 -L 1 sh -c './run.sh $0 $1 $2 $(pwd)/$3'
# 保存物(d172・d173/mid・d174/sm)と今日の raw175 を合わせて集計する
python analyze175.py <d172> <d173/mid> <d174/sm>
# 残りの偏り・葉 63 対 127・単純な方を優先する版
python extra175.py

実行結果(実測)

# ============ 実行環境: macOS (M1, メモリ8GB)・Day172 と同じ uv venv(Python 3.13.12・scikit-learn 1.9.1・numpy 2.5.3・pandas 3.0.6・lightgbm 4.7.0)
#   40 ワールド × 5 fold × 乱数なし3本 = 600 fit。OMP_NUM_THREADS=1・n_jobs=1
#   壁時計: 4 並行。最初の一括実行が 600 秒の時間上限で止められ(32 ワールド完了・残り8は未着手)、残り8を 84 秒で回した。合計 約 684 秒、1ワールド 47〜82 秒
#   集計 analyze175.py 11.9 秒
=== 表0 再現(決定9): 今日の lgb_det/leaf63 と保存物の最大絶対差 ===
      lgb_oof  lgb_ev  l63_oof  l63_ev
kind                                  
中雑音       0.0     0.0      NaN     NaN
低雑音       0.0     0.0      NaN     NaN
小得        0.0     0.0      0.0     0.0

=== 表1 真の差 T5−T5(lgb_det)(×1e-4、ワールド平均±SE)/良い割合 ===
[低雑音] leaf63 -3.67±0.34 (1.00)  leaf127 -3.50±0.50 (0.94)  leaf63_b7 +5.44±0.33 (0.00)  hgb_default +0.35±0.23 (0.44)
[小得] leaf63 -12.72±0.29 (1.00)  leaf127 -22.96±0.44 (1.00)  leaf63_b7 -2.47±0.37 (0.92)  hgb_default -0.07±0.18 (0.67)
[中雑音] leaf63 -16.47±0.55 (1.00)  leaf127 -31.74±0.54 (1.00)  leaf63_b7 -6.51±0.45 (1.00)  hgb_default -0.11±0.28 (0.50)

=== 表1b 乱数なし3本の 生CV差・補正CV差・真の差(×1e-4、ワールド平均)とずれのSD ===
低雑音  leaf63: 生 +2.02 補正 -3.34 真 -3.67 ずれSD 4.29 生ずれSD 4.36  leaf127: 生 +10.46 補正 -1.46 真 -3.50 ずれSD 5.14 生ずれSD 5.25
小得  leaf63: 生 -6.74 補正 -13.23 真 -12.72 ずれSD 3.54 生ずれSD 3.58  leaf127: 生 -7.05 補正 -21.76 真 -22.96 ずれSD 5.00 生ずれSD 5.10
中雑音  leaf63: 生 -10.23 補正 -17.32 真 -16.47 ずれSD 3.17 生ずれSD 3.17  leaf127: 生 -15.16 補正 -31.74 真 -31.74 ずれSD 6.00 生ずれSD 6.09

=== 表2 基準選び(乱数なし3本の中で補正CV最良を選ぶ): 的中と損(×1e-4)===
[低雑音] 的中 5/16 損 0.88 最大 4.53  選んだ {'leaf63': 13, 'leaf127': 2, 'lgb_det': 1}  真の最良 {'leaf127': 8, 'leaf63': 8}
     生CVで選ぶと: 的中 1/16 損 3.37
[小得] 的中 12/12 損 0.00 最大 0.00  選んだ {'leaf127': 12}  真の最良 {'leaf127': 12}
     生CVで選ぶと: 的中 7/12 損 4.28
[中雑音] 的中 12/12 損 0.00 最大 0.00  選んだ {'leaf127': 12}  真の最良 {'leaf127': 12}
     生CVで選ぶと: 的中 11/12 損 1.31

=== 表3 方針ごとの損(全14本の真の最良との差、×1e-4、ワールド平均±SE)と3世界の最悪の超過損 ===
kind               低雑音          小得         中雑音  最悪の超過
pol                                                  
全14本の補正CV最良  0.88±0.31     0.0±0.0     0.0±0.0   0.50
決定15 m=0     0.88±0.31     0.0±0.0     0.0±0.0   0.50
決定15 m=1     0.88±0.31     0.0±0.0     0.0±0.0   0.50
決定15 m=2     0.88±0.31     0.0±0.0     0.0±0.0   0.50
決定15 m=3     0.88±0.31     0.0±0.0     0.0±0.0   0.50
決定15 m=4     0.88±0.31     0.0±0.0     0.0±0.0   0.50
決定15 m=6     0.88±0.31     0.0±0.0     0.0±0.0   0.50
決定15の基準だけ    0.88±0.31     0.0±0.0     0.0±0.0   0.50
常にleaf63     0.38±0.14  10.24±0.34  15.28±0.34  15.28
決定14 m=0     4.85±0.65  21.02±0.63   25.24±0.5  25.24
決定14 m=1      4.4±0.36  21.08±0.61   25.24±0.5  25.24
決定14 m=2     4.24±0.32  21.14±0.64   25.24±0.5  25.24
決定14 m=3      4.2±0.32  21.25±0.64  25.76±0.67  25.76
決定14 m=4     4.09±0.36  21.45±0.68  25.76±0.67  25.76
決定14 m=6     4.05±0.37   22.73±0.4  26.96±0.94  26.96
常にlgb_det    4.05±0.37  22.96±0.44  31.74±0.54  31.74

=== 表4 選ばれた候補(m=2)と真の最良 ===
[低雑音] 決定14 m=2: {'lgb_det': 25, 'hgb_default': 4, 'bag08': 3}
[低雑音] 決定15 m=2: {'leaf63': 26, 'leaf127': 4, 'lgb_det': 2}
[低雑音] 真の最良: {'leaf127': 16, 'leaf63': 16}
[小得] 決定14 m=2: {'leaf63_b7': 14, 'lgb_det': 9, 'bag08': 1}
[小得] 決定15 m=2: {'leaf127': 24}
[小得] 真の最良: {'leaf127': 24}
[中雑音] 決定14 m=2: {'leaf63_b7': 24}
[中雑音] 決定15 m=2: {'leaf127': 24}
[中雑音] 真の最良: {'leaf127': 24}

=== 表5 ワールド対の差 決定15 m=2 − 決定14 m=2(×1e-4)===
[低雑音] 決定15 m=2 − 決定14 m=2: -3.36±0.43
[低雑音] 決定15 m=2 − 常にleaf63: +0.50±0.32
[低雑音] 決定15 m=2 − 決定15の基準だけ: +0.00±0.00
[低雑音] 決定15 m=0 − 決定15 m=2: +0.00±0.00
[小得] 決定15 m=2 − 決定14 m=2: -21.14±0.64
[小得] 決定15 m=2 − 常にleaf63: -10.24±0.34
[小得] 決定15 m=2 − 決定15の基準だけ: +0.00±0.00
[小得] 決定15 m=0 − 決定15 m=2: +0.00±0.00
[中雑音] 決定15 m=2 − 決定14 m=2: -25.24±0.50
[中雑音] 決定15 m=2 − 常にleaf63: -15.28±0.34
[中雑音] 決定15 m=2 − 決定15の基準だけ: +0.00±0.00
[中雑音] 決定15 m=0 − 決定15 m=2: +0.00±0.00
# ---- extra175.py ----
[低雑音] leaf63: 補正の残り偏り +0.33±1.07  生の偏り +5.69±1.09
[低雑音] leaf127: 補正の残り偏り +2.04±1.28  生の偏り +13.96±1.31
[低雑音] A_test(×1e-4, 二乗の単位) {'lgb_det': 3.957, 'leaf63': 5.632, 'leaf127': 7.713}
[低雑音] 真 leaf127−leaf63 +0.17±0.33  補正CV +1.87  符号一致 5/16  相関 -0.23
[小得] leaf63: 補正の残り偏り -0.51±1.02  生の偏り +5.99±1.03
[小得] leaf127: 補正の残り偏り +1.20±1.44  生の偏り +15.91±1.47
[小得] A_test(×1e-4, 二乗の単位) {'lgb_det': 4.955, 'leaf63': 7.523, 'leaf127': 10.779}
[小得] 真 leaf127−leaf63 -10.24±0.34  補正CV -8.53  符号一致 12/12  相関 -0.04
[中雑音] leaf63: 補正の残り偏り -0.86±0.91  生の偏り +6.24±0.92
[中雑音] leaf127: 補正の残り偏り -0.00±1.73  生の偏り +16.58±1.76
[中雑音] A_test(×1e-4, 二乗の単位) {'lgb_det': 5.874, 'leaf63': 9.021, 'leaf127': 13.218}
[中雑音] 真 leaf127−leaf63 -15.28±0.34  補正CV -14.42  符号一致 12/12  相関 -0.17

--- 単純な方を優先する版(葉 31→63→127 の順に、補正CVが m' 以上良いときだけ大きい方へ進む): 全14本の最良との損(×1e-4、s0/s1 平均)---
kind   低雑音   小得  中雑音  最悪の超過(決定15 m'=0 の 0.88/0/0 を含む表での)
mp                                                      
0     0.88  0.0  0.0                                0.00
1     1.43  0.0  0.0                                0.55
2     1.26  0.0  0.0                                0.38
3     2.41  0.0  0.0                                1.53
4     3.25  0.0  0.0                                2.37
pick  leaf127  leaf63  lgb_det
mp                            
0           4      26        2
1           4      20        8
2           2      22        8
3           4      12       16
4           4       6       22

タスクC(分析の模範)

  1. 生の CV は葉の多い候補を大きく悪く見せ、補正CVはそれをほぼ消す。 生の偏りは leaf63 で +5.7〜+6.2、leaf127 で +14.0〜+16.6(3世界、どれも SE 1 前後)。A_test も葉 31→63→127 で約 1.4 倍・2 倍に増え、偏りの増え方と合う。補正後の残りは leaf63 −0.86〜+0.33、leaf127 −0.00〜+2.04 で、どれも 2SE 未満。生の CV で基準を選ぶと低雑音で的中 1/16・損 3.37(補正CV は 5/16・0.88)。低雑音の生の CV は leaf127 を +10.46 と「悪い」と言うが、真は −3.50 で良い
  2. 低雑音の損 0.88 は、leaf63 と leaf127 の引き分けを補正CVが見分けられないことから来る。 真の差 leaf127−leaf63 は +0.17±0.33(真の最良は 16 対 16 で半々)。補正CVの差は +1.87 で、ワールドごとの真の差との相関は −0.23、符号一致 5/16。つまり差が 2 を切ると補正CV は当てずっぽう。それでも損は小さい(引き分けなのでどちらを選んでも近い)。0.88 の内訳は、lgb_det を選んだ2件(各 4.53)が 0.28、leaf63 を選んだ26件(平均 0.47)が 0.38、leaf127 を選んだ4件(平均 1.70)が 0.21。「常に leaf63」は低雑音で 0.38 だが、小得 10.24・中雑音 15.28 で leaf127 を逃す。ワールド対で決定15 − 常にleaf63 は低雑音 +0.50±0.32(1.6SE、有意ではない)、小得 −10.24、中雑音 −15.28。決定15 を採る
  3. 決定15 の印を △→○。決定14 は「休眠」に。 3世界の最悪の超過損は決定15 が 0.50、決定14(基準 lgb_det 固定)が 25.24、常に lgb_det が 31.74。決定15 のあと、m を 0〜6 のどれにしても損は小数2桁まで同じ。今日の乱数ありの候補はすべて葉31以下か leaf63_b7(抜き出しで悪化)なので、強い基準を超えられない。決定14 は「乱数ありの候補が基準と同じ構造で作られたとき」にだけ意味を持つ。今の候補一覧では効かないが、消すのではなく「乱数ありの候補を基準の構造(葉 127)に合わせて作り直したら測り直す」と条件を付けて残す

タスクD(決定)

#決定採った案採らなかった案と理由前提が変わったら
15(改訂、印 △→○)基準の選び方乱数なしの候補(葉 31・63・127)を補正CVで比べ、最良を基準にする。比べるのに余裕は付けない(m'=0)(a) 常に leaf63(低雑音で 0.38 と一番良いが、小得・中雑音で 10〜15 の損。最悪の超過 15.28)。(b) 単純な方を優先し m' 以上良いときだけ葉を増やす(m'=2 で低雑音 1.26、m'=0 の 0.88 より悪い。lgb_det に留まる件が 2→8 に増える)。(c) 生の CV で選ぶ(低雑音で的中 1/16・損 3.37)葉 127 は小得・中雑音でまだ良くなり続けている(−22.96・−31.74)。上限(255・511)と min_child_samples を足して、どこで止まるかを測る。差が 2 未満の候補どうしは補正CVで見分けられないので、候補を細かく刻んでも得は無い
14(据え置き、印 ○→「休眠」)基準からの乗り換え(m=2)文面はそのまま。ただし決定15 の基準のあとでは今日の3世界で1回も発動しなかった(m=0〜6 で損が同じ)(a) 削除(乱数ありの候補を葉 127 で作り直すと、また効く場面が出るかもしれない)。(b) m を下げる(発動しないので根拠が無い)乱数ありの候補を基準の構造(葉 63/127)で作り直したら、m を測り直す
13(補正CV で比べる)印 ◎ のまま乱数なしの葉 63・127 でも生の偏り +6・+15 をほぼ消した(残り 2SE 未満)—葉 255 以上で A_test がさらに大きくなったとき、残りの偏り(leaf127 低雑音で +2.04±1.28)が育たないかを見る

設計書の印の更新:

決定前後根拠
13(補正CV)◎◎葉 63・127 の生の偏り +5.7〜+16.6 を、補正後 −0.86〜+2.04(2SE 未満)に
14(乗り換え m=2)○休眠決定15 のあと m=0〜6 で損が同じ(乗り換え0回)
15(基準を乱数なし候補から選ぶ)△○3世界の最悪の超過損 0.50(決定14 25.24、常にleaf63 15.28)。◎ にしないのは、葉 127 が上限でない(まだ良くなる)ことと、実データを測っていないため

📊 結果の可視化

3種類の世界の混ぜ方を知らないときの最悪の超過損(×10⁻⁴、短いほど良い)決定15 は m=0〜6 のどれでも同じ値(乗り換えが1回も起きない)。0.50 は低雑音の引き分け(leaf63 対 leaf127)から決定15(基準を補正CVで選ぶ)0.50常に leaf6315.28決定14 m=2(基準 lgb_det 固定)25.24常に lgb_det31.74
葉を増やした乱数なしの候補の真の差(T5 − T5(lgb_det)、×10⁻⁴、左へ長いほど良い)水色=leaf63/緑=leaf127。3種類の世界とも lgb_det(葉31)より良い。低雑音では 63 と 127 がほぼ引き分け(+0.17±0.33)0 = lgb_det低雑音-3.67-3.50小得-12.72-22.96中雑音-16.47-31.74
世界ごとの損(全14本の真の最良との差、×10⁻⁴、seed 組 s0/s1 とワールドの平均)決定15常にleaf63決定14 m=2常にlgb_det低雑音0.880.384.244.05小得0.0010.2421.1422.96中雑音0.0015.2825.2431.74

1枚目: 3世界での最悪の超過損。決定15 が 0.50 で、基準を lgb_det に固定した決定14(25.24)の約50分の1 → 決定15 の印を △→○。2枚目: 葉を増やした候補の真の差。低雑音でも良く、Day174 の「低雑音では悪いかも」は外れ。3枚目: 世界ごとの損。決定15 は小得・中雑音で 0、低雑音でだけ常に leaf63 に 0.50 負ける(1.6SE、有意でない)。

🪜 Step-by-Step 解説

Step 1: 作るものを最小にする

乱数ありの候補は Day172〜174 で作って保存してある。同じワールドは同じ乱数(3000+番号)・同じ雑音で作り直せるので、新しく作るのは乱数なしの3本だけでよい。乱数なしなら seed 組も1つで済む。これで 600 fit・約 11 分に収まった(全部作り直すと 40 ワールド × 14 本 × 2 seed で約 1 時間半の見込み)。

Step 2: 先に再現を確かめる

今日の lgb_det と保存物の lgb_det を、OOF と fold 別の test 予測の両方で比べた。3世界とも最大差 0.0。小得では今日の leaf63 と Day174 の leaf63 も 0.0。ラベル ytr・yev も np.array_equal で一致を assert している。これが崩れていたら、保存物と今日の予測を混ぜた表はすべて意味を失う。

Step 3: 真の差を先に見る

方針を比べる前に、候補そのものの真の差を見た。leaf127 は小得 −22.96・中雑音 −31.74 と、leaf63 よりさらに大きく良い。低雑音でも leaf63 −3.67・leaf127 −3.50 で lgb_det より良い。Day174 で置いた「低雑音では葉を増やすと悪いかも」は外れで、Day172 の leaf63_b7 +5.44 は抜き出しの害だった。

Step 4: 偏りを生と補正で分ける

生の CV は葉の多い候補を最大 +16.6 悪く見せていた(Day171 の「1モデル対5モデル平均」と同じ仕組みで、葉が多いほど1本ごとのばらつきが大きく、平均の得が大きい)。補正CV でこれはほぼ消える。生の CV で基準を選んでいたら、低雑音で leaf127 を「悪い」と判断していた。

Step 5: 方針の損を3世界で並べる

決定15 は小得・中雑音で損 0(24/24 で leaf127 を選んだ)、低雑音で 0.88。決定14(基準 lgb_det 固定)は 4.24・21.14・25.24。決定15 のあと m を動かしても損は1つも変わらなかった。

Step 6: 引き分けの扱いを確かめる

低雑音の 0.88 を減らせるか、「単純な方を優先し、m' 以上良いときだけ葉を増やす」版を試した。m'=2 で 1.26、m'=4 で 3.25 と、どれも m'=0 より悪い。余裕を付けると、引き分けの leaf63/leaf127 の間だけでなく、本当に悪い lgb_det に留まる件が増えるため。引き分けは損が小さいので放っておき、本当に大きい差だけを確実に取るのが今日の結論。

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

なぜ葉が多いほど生の CV が悪く見えるか(写真の例え)。 手ぶれのある写真を1枚だけ見る(OOF=1モデル)のと、5枚重ねて平均した写真を見る(提出=5モデル平均)のとでは、5枚重ねた方がくっきりする。葉の多い木は1枚ごとの手ぶれ(予測のばらつき)が大きいので、重ねたときの改善も大きい。生の CV は「1枚の写真」で採点するので、手ぶれの大きい候補ほど不利に見える。補正CV は、5枚の写真がどれだけ互いにずれているか(A_test)を測って、その分を差し引く。今日の A_test は葉 31 を 1 とすると、葉 63 で約 1.4〜1.5、葉 127 で約 1.9〜2.3 倍で、生の偏り(約 6 → 約 15)の増え方とそろう。

「差が 2 を切ると当てずっぽう」の意味。 補正CV の差は、真の差に「ワールドごとのずれ」(SD 3〜6)が足されたもの。真の差が 0.17 しかないと、ずれの方がずっと大きいので、どちらが良く見えるかは偶然で決まる(相関 −0.23)。真の差が 10 あれば(小得・中雑音の leaf127 対 leaf63)、ずれに埋もれず 12/12 当たる。見分けられる差の大きさには下限がある。その下限より小さい差を選び分けようとしても得は出ないが、損も小さい。

🏆 Kaggleでの実践的な使い方

  • 基準モデルの葉の数(木の大きさ)は、乱数の無い2〜3本を並べて補正CVで決める。 既定の num_leaves=31 は多くのテーブルコンペで小さすぎることがある。今日の縮小シミュレータでは、雑音の大きさに関係なく 63 以上が良かった
  • 生の CV で木の大きさを比べない。 1モデルの OOF は大きい木を不利に見せる(今日は最大 +16.6)。提出が fold モデルの平均なら、比べる指標もそれに合わせる(決定13)。手早く済ませるなら、test 予測の fold 間分散を候補ごとに出しておくだけでよい(ラベル不要)
  • 候補が強くなると「選び方の細かい調整」は効かなくなる。 今日は基準を強くしただけで、余裕 m の調整(Day173〜174 で 3 日かけた)が意味を失った。調整を詰める前に、候補一覧の一番弱いところ(今は葉の上限)を先に見る
  • 引き分けは放っておく。 CV の差が SE と同じくらいの候補どうしを選び分けようと手を足しても(今日の m' 版)、本当に悪い候補に留まる件が増えて損が増えた

⚠️ よくある誤解・ミス

誤解・ミスなぜ起こるか正しい理解
雑音が小さいデータでは大きい木は過学習で悪い「複雑なモデルは過学習」という一般論今日の低雑音でも leaf63 は 16/16 で良い。悪く見えたのは抜き出し 0.7 の害と、生の CV の偏り
生の CV が悪いなら大きい木はやめるCV をそのまま信じる生の CV は低雑音で leaf127 を +10.46 悪く見せたが、真は −3.50。fold 平均で提出するなら補正CVで比べる
余裕 m を付けた方が安全外れの損だけを見る基準選びに余裕 m' を付けると、低雑音で lgb_det に留まる件が 2→8 に増え、損 0.88→1.26
補正CV は小さい差も見分けられる偏りを消した=精度も上がったと考える偏り(平均)は消えても、ワールドごとのずれ(SD 3〜6)は残る。真の差 0.17 は符号一致 5/16
決定14 が発動しないのはバグ何も起きないことを失敗と読む基準が強くなり、乱数ありの候補がどれも届かなくなった結果。表3 で m=0〜6 の損が同じことで確かめた

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

  • 葉 255・511 と min_child_samples: leaf127 が小得・中雑音でまだ良くなり続けており、上限が分かっていない。今日は決定15 を3世界で確かめるのが目的で、候補を広げると「選び方」と「候補の中身」が混ざるため分けた。次回の最優先
  • 乱数ありの候補を葉 127 で作り直すこと: 決定14 が休眠したのは候補一覧のせいか、仕組みのせいかは未分離。作り直すと 40 ワールド × 9 本 × 2 seed で 1 時間以上かかる
  • 候補数 K と選択の上振れ: 今日の基準選びは3本からなので上振れは小さい。K 依存は持ち越し
  • 公開LB と 2 本目の枠、avg3(3 seed 平均)での損: 測っていない
  • 実データ(S5E5): 縮小シミュレータの上の数字のみ。S5E5 のルール確認・公開割合・敵対的AUC は Day171 で利用者の作業として切り出したまま
  • 持ち越し: 系統別の m(et)、OOF 側と test 側の seed の揺れの分離、noKeytel・raw7 の補正後のずれ、補正CV と入れ子CV の比較、A_test の推定誤差、Day170 のハイブリッド・狭い10本の出し直し、SE 個数差と分割の新しさ(Day165)、Day162 の選択バイアス、真の効果ちょうど0の偽採用率、Nadeau & Bengio・Krogh & Vedelsby 原典(今日も記憶による)

🚀 次のステップ

  • 発展: 乱数なしの葉 127・255・511 × min_child_samples 20/50 を3世界に足し、補正CVの残りの偏り(leaf127 低雑音 +2.04)が葉とともに育つか、どこで真の改善が止まるかを測る
  • 発展: 乱数ありの候補を葉 127 の上で作り直し(bagging 0.8・列抽出 0.7)、決定14 が再び効く場面があるかを確かめる
  • 次回予告: Day 176 — Playground Series 実戦投入⑪(木の大きさの上限: 葉 127/255/511 × min_child_samples 20/50 を乱数なしで作り、決定15 の候補一覧をどこまで広げると得が止まるか、補正CV の残りの偏りが育つかを測る)。月替わりテーマ3「画像(CV)系コンペへの応用」は 10/25 から

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

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