x402-demand / code /baselines.py
Priceman614's picture
x402-demand v1: listing-only checkpoint, card, figures, metrics, process doc, code
8bf7463 verified
Raw History Blame Contribute Delete
2.61 kB
"""Non-neural baselines. A published neural checkpoint has to beat these."""
from __future__ import annotations
import json
from pathlib import Path
import lightgbm as lgb
import numpy as np
import pandas as pd
from sklearn.isotonic import IsotonicRegression
from sklearn.linear_model import LogisticRegression
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from common import NUMERIC, report, split
df = pd.read_parquet("data/rows.parquet")
df["network_cat"] = df.network.astype("category")
results: dict = {}
Path("runs").mkdir(exist_ok=True)
for h in (7, 14):
lab = f"paid_{h}d"
tr, va, te = split(df, h)
print(f"\n== paid_{h}d train {len(tr):,} val {len(va):,} test {len(te):,} "
f"(test hosts {te.host.nunique()}, unseen in train)")
res = {}
# 1. Recency rule: the fresher the last paid call, the likelier the next one.
rec = te.days_since_last_call.fillna(99).to_numpy()
iso = IsotonicRegression(out_of_bounds="clip", increasing=False).fit(
va.days_since_last_call.fillna(99), va[lab])
res["recency_rule"] = report(te, iso.predict(rec), h)
# 2. Logistic regression on numeric features.
X = lambda d: d[NUMERIC].fillna(-1).to_numpy()
lr = make_pipeline(StandardScaler(), LogisticRegression(max_iter=2000)).fit(X(tr), tr[lab])
res["logreg"] = report(te, lr.predict_proba(X(te))[:, 1], h)
# 3. LightGBM on numeric + network.
feats = NUMERIC + ["network_cat"]
gbm = lgb.LGBMClassifier(n_estimators=600, learning_rate=0.03, num_leaves=31,
min_child_samples=50, subsample=0.8, subsample_freq=1,
colsample_bytree=0.8, verbose=-1)
gbm.fit(tr[feats], tr[lab], eval_set=[(va[feats], va[lab])],
callbacks=[lgb.early_stopping(50, verbose=False)])
p = gbm.predict_proba(te[feats])[:, 1]
res["lightgbm"] = report(te, p, h)
imp = sorted(zip(feats, gbm.booster_.feature_importance("gain")), key=lambda x: -x[1])
res["lightgbm_top_features"] = [(f, round(float(g), 1)) for f, g in imp[:10]]
if h == 7:
gbm.booster_.save_model("runs/lightgbm_paid7d.txt")
np.save("runs/lightgbm_paid7d_test_pred.npy", p)
for k, v in res.items():
if k.endswith("features"):
print(" ", k, v[:6])
continue
print(f" {k:14s}", " ".join(f"{seg}: auc {m['auc']} ap {m['ap']} ece {m['ece']}"
for seg, m in v.items()))
results[f"paid_{h}d"] = res
Path("runs/baselines.json").write_text(json.dumps(results, indent=1))