SniperGold_ML/ml/p3/baseline/train_tree.py

96 lines
3.6 KiB
Python

# -*- coding: utf-8 -*-
"""P3-S.18 — TREE BASELINE (shallow decision tree + constrained boosting).
Intentionally small: DecisionTree(max_depth=3) and GradientBoosting
(n_estimators=40, max_depth=2, lr=0.03). No broad hyperparameter search.
Binary target: WIN vs LOSS (leads only). Same split/standardization policy;
trees are scale-invariant, scaler applied for consistency of the pipeline.
"""
import csv
import json
import os
import sys
import numpy as np
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.preprocessing import StandardScaler
from sklearn.tree import DecisionTreeClassifier
HERE = os.path.dirname(os.path.abspath(__file__))
OUT = os.path.join(HERE, "output")
sys.path.insert(0, HERE)
sys.path.insert(0, os.path.normpath(os.path.join(HERE, "..", "setup_dataset")))
import prepare_dataset as PD # noqa: E402
SEED = 42
def _setup():
ctx = PD.load_verified_population()
rows = PD.build_rows(ctx)
bin_rows = [r for r in rows if r["lead"] and r["outcome"] in PD.BINARY_CLASSES]
bin_rows.sort(key=lambda r: (r["creation_bar"], r["setup_id"]))
tr, va, te, _ = PD.temporal_split(rows)
split_map = {}
for part, tag in ((tr, "train"), (va, "val"), (te, "test")):
for r in part:
split_map[r["setup_id"]] = tag
X = np.asarray([[r["feature_" + k] for k in PD.FEATURE_COLS]
for r in bin_rows], dtype=float)
y = np.asarray([1.0 if r["outcome"] == "WIN" else 0.0
for r in bin_rows], dtype=float)
splits = np.asarray([split_map[r["setup_id"]] for r in bin_rows])
scaler = StandardScaler().fit(X[splits == "train"])
Xs = scaler.transform(X)
return Xs, y, splits, bin_rows, tr, va, te, rows
def main():
os.makedirs(OUT, exist_ok=True)
Xs, y, splits, bin_rows, tr, va, te, rows = _setup()
models = {
"tree_depth3": DecisionTreeClassifier(max_depth=3, random_state=SEED,
min_samples_leaf=5),
"boost_small": GradientBoostingClassifier(
n_estimators=40, max_depth=2, learning_rate=0.03,
random_state=SEED),
}
preds = {}
for name, clf in models.items():
clf.fit(Xs[splits == "train"], y[splits == "train"])
preds[name] = clf.predict_proba(Xs)[:, 1]
rows_out = []
for i, r in enumerate(bin_rows):
d = {"setup_id": r["setup_id"], "split": splits[i],
"outcome": r["outcome"], "y": int(y[i])}
for name in models:
d["pred_" + name] = float(preds[name][i])
rows_out.append(d)
with open(os.path.join(OUT, "p3_s18_predictions_tree.csv"), "w",
newline="", encoding="utf-8") as f:
w = csv.DictWriter(f, fieldnames=list(rows_out[0].keys()))
w.writeheader()
for r in rows_out:
w.writerow(r)
with open(os.path.join(OUT, "p3_s18_tree_model.json"), "w",
encoding="utf-8") as f:
json.dump({
"models": list(models.keys()),
"config": {"tree": "max_depth=3,min_samples_leaf=5",
"boost": "n_estimators=40,max_depth=2,lr=0.03"},
"feature_sha16": PD.make_manifest(rows, tr, va, te,
True)["feature_sha16"],
"seed": SEED,
"feature_importance": {
"tree_depth3": dict(zip(PD.FEATURE_COLS, models[
"tree_depth3"].feature_importances_.tolist()))},
}, f, indent=2)
print("[saved] p3_s18_predictions_tree.csv / tree_model.json")
return 0
if __name__ == "__main__":
sys.exit(main())