drishti / experiments /mae_tuning.py
rittika03's picture
DRISHTI deploy
c8a8b27
Raw
History Blame Contribute Delete
4.8 kB
"""Offline experiment: can we lower validation MAE without breaking the pipeline?
Tests objective choices and extra features on the SAME time-split as production,
so the comparison is apples-to-apples. Prints a table; ships nothing.
"""
import warnings
import numpy as np
import pandas as pd
import lightgbm as lgb
from src import config
from src.data_prep import load_clean
from src.features import add_h3
from src.model import build_panel, FEATURES as BASE_FEATURES
warnings.filterwarnings("ignore")
np.random.seed(config.RANDOM_STATE)
print("Loading + indexing data ...")
df = add_h3(load_clean())
panel, keep_cells, all_dates = build_panel(df)
# ---- add candidate features on top of the production panel ----
panel = panel.sort_values(["h3", "date"]).reset_index(drop=True)
g = panel.groupby("h3")["y"]
yshift = g.shift(1)
# extra short/seasonal lags
for L in [2, 3, 21]:
panel[f"lag_{L}"] = g.shift(L)
# rolling std (volatility) + a mid window mean
for W in [7, 14, 28]:
panel[f"roll_std_{W}"] = (yshift.groupby(panel["h3"]).rolling(W, min_periods=2)
.std().reset_index(level=0, drop=True))
panel["roll_mean_14"] = (yshift.groupby(panel["h3"]).rolling(14, min_periods=1)
.mean().reset_index(level=0, drop=True))
# EWMA (recency-weighted level)
panel["ewm_7"] = (yshift.groupby(panel["h3"]).ewm(span=7, min_periods=1)
.mean().reset_index(level=0, drop=True))
# same-weekday mean over the last 4 occurrences (weekly seasonality, no leak)
sd = panel.groupby(["h3", "dow"])["y"].shift(1)
panel["samedow_mean4"] = (sd.groupby([panel["h3"], panel["dow"]])
.rolling(4, min_periods=1).mean()
.reset_index(level=[0, 1], drop=True))
# expanding cell mean (shifted -> no leak)
panel["cell_expmean"] = (g.apply(lambda s: s.shift(1).expanding().mean())
.reset_index(level=0, drop=True))
panel["roll_std_7"] = panel["roll_std_7"].fillna(0)
panel["roll_std_14"] = panel["roll_std_14"].fillna(0)
panel["roll_std_28"] = panel["roll_std_28"].fillna(0)
EXTRA = ["lag_2", "lag_3", "lag_21", "roll_std_7", "roll_std_14", "roll_std_28",
"roll_mean_14", "ewm_7", "samedow_mean4", "cell_expmean"]
model_df = panel.dropna(subset=[f"lag_{max(config.LAGS)}"]).copy()
cutoff = all_dates.max() - pd.Timedelta(days=config.VALID_DAYS)
train = model_df[model_df["date"] <= cutoff]
valid = model_df[model_df["date"] > cutoff]
yv = valid["y"].values
base_mae = float(np.mean(np.abs(valid["lag_7"].values - yv)))
print(f"rows train/valid: {len(train):,}/{len(valid):,} | "
f"same-weekday baseline MAE: {base_mae:.3f}\n")
def run(feats, objective, label, log=False, extra_params=None):
p = dict(metric="mae", learning_rate=0.05, num_leaves=63, min_data_in_leaf=50,
feature_fraction=0.8, bagging_fraction=0.8, bagging_freq=1,
seed=config.RANDOM_STATE, verbose=-1, objective=objective)
if extra_params:
p.update(extra_params)
ytr = np.log1p(train["y"]) if log else train["y"]
dtr = lgb.Dataset(train[feats], ytr)
dva = lgb.Dataset(valid[feats], (np.log1p(valid["y"]) if log else valid["y"]),
reference=dtr)
m = lgb.train(p, dtr, num_boost_round=1200, valid_sets=[dva],
callbacks=[lgb.early_stopping(80), lgb.log_evaluation(0)])
pred = m.predict(valid[feats], num_iteration=m.best_iteration)
if log:
pred = np.expm1(pred)
mae = float(np.mean(np.abs(np.clip(pred, 0, None) - yv)))
imp = 100 * (base_mae - mae) / base_mae
print(f" {label:<46} MAE={mae:.3f} ({imp:+.1f}% vs baseline) it={m.best_iteration}")
return mae
print("=== production config (reproduce) ===")
run(BASE_FEATURES, "regression", "L2 objective, base features [CURRENT]")
print("\n=== change the training objective only (base features) ===")
run(BASE_FEATURES, "regression_l1", "L1/MAE objective")
run(BASE_FEATURES, "huber", "Huber objective")
run(BASE_FEATURES, "fair", "Fair objective")
print("\n=== add features ===")
run(BASE_FEATURES + EXTRA, "regression", "L2 + extra features")
run(BASE_FEATURES + EXTRA, "regression_l1", "L1 + extra features")
run(BASE_FEATURES + EXTRA, "huber", "Huber + extra features")
print("\n=== best combo + mild regularization ===")
run(BASE_FEATURES + EXTRA, "regression_l1", "L1 + extra + L2 reg + smaller leaves",
extra_params=dict(lambda_l2=1.0, num_leaves=48, min_data_in_leaf=80))
run(BASE_FEATURES + EXTRA, "huber", "Huber + extra + reg",
extra_params=dict(lambda_l2=1.0, num_leaves=48, min_data_in_leaf=80))
print("\n=== log1p target variants ===")
run(BASE_FEATURES + EXTRA, "regression", "log1p + L2 + extra", log=True)
run(BASE_FEATURES + EXTRA, "regression_l1", "log1p + L1 + extra", log=True)